Compare commits

..

2543 Commits

Author SHA1 Message Date
github-actions c312dbb707 Tagging Crypto-Node.js for release 2023-04-26 12:03:17 +00:00
Andrew Ferrazzutti 8e0559963e Restore "Install musl-gcc for linux-musl nodejs releases"
This reverts commit cfc6ec2c0d.
2023-04-26 20:25:57 +09:00
Andrew Ferrazzutti 4bbea71d51 Add comment to explain why to use Ubuntu LTS-1 2023-04-26 18:45:18 +09:00
Andrew Ferrazzutti 80de0f0bbe Build Node bindings against Ubuntu 20.04
Fixes #1808
2023-04-26 18:45:16 +09:00
Kévin Commaille 51a2d101b6 base: Remove RoomMemberships::UNKNOWN/KNOWN
The unknown filter can be difficult to match with MembershipState,
depending on the store implementation.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-26 10:12:22 +02:00
Kévin Commaille f7e8b22646 memory-store: indexeddb: Store memberships as MembershipState
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-26 10:12:22 +02:00
Kévin Commaille 252f4cb9a2 sled: Store memberships as strings
Storing as bitset is not future-proof.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-26 10:12:22 +02:00
Kévin Commaille a511500e6e indexeddb: Store memberships as strings
Storing as bitset is not future-proof.
Just fix the latest migration. We assume no-one used it yet.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-26 10:12:22 +02:00
Kévin Commaille 3081dabb66 base: Add method to match RoomMemberships with MembershipState
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-26 10:12:22 +02:00
Ivan Enderlin 770d50e2ce Merge pull request #1822 from AndrewFerr/af/update-nodejs-version
Update supported Node.js versions
2023-04-26 08:37:20 +02:00
Kévin Commaille ae79fd0af5 sdk: Deprecate Common::(active/joined)_members(_no_sync)
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-25 18:03:53 +02:00
Kévin Commaille b8f06ec4a1 sdk-base: Deprecate Room::(active/joined)_members
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-25 18:03:53 +02:00
Kévin Commaille fe6424f48c sdk: Implement Common::(active/joined)_members_no_sync with members_no_sync
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-25 18:03:53 +02:00
Kévin Commaille 2010c180a8 base-sdk: Implement Room::(active/joined)_members with members
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-25 18:03:53 +02:00
Kévin Commaille e98fbfce9a sdk: Allow to filter Common::members(_no_sync) by membership state
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-25 18:03:53 +02:00
Kévin Commaille f68b45aa49 base-sdk:Allow to filter Room::members by membership state
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-25 18:03:53 +02:00
Kévin Commaille 81a543e15b sdk-base: Deprecate get_invited_user_ids and get_joined_user_ids
get_user_ids can be used instead

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-25 18:03:53 +02:00
Kévin Commaille 4ffa20abde sdk-base: Allow to filter get_user_ids results by any membership state
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-25 18:03:53 +02:00
Kévin Commaille 58a1ad1b93 base: Use single map for all user IDs in memory store
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-25 18:03:53 +02:00
Andrew Ferrazzutti 564549e8af Update Node bindings' "engines"
Signed-off-by: Andrew Ferrazzutti <andrewf@element.io>
2023-04-25 23:59:22 +09:00
Andrew Ferrazzutti 46f69f7efd Update supported Node.js versions
Signed-off-by: Andrew Ferrazzutti <andrewf@element.io>
2023-04-25 23:51:53 +09:00
Jonas Platte c897f0ccaf ffi: Print location for tracing events 2023-04-25 12:19:55 +02:00
Jonas Platte bd3a37791b Distinguish events from live sync and sync cache in debug string 2023-04-25 11:50:06 +02:00
Simon Farre b105359b27 Expose fetch_event_details to FFI
Signed-off-by: Simon Farre <simon.farre.cx@gmail.com>
2023-04-24 20:50:06 +00:00
Ivan Enderlin c5c8ac8a7c feat(sdk): Add support for bump_event_types in SlidingSync
feat(sdk): Add support for `bump_event_types` in `SlidingSync`
2023-04-24 20:23:27 +02:00
Ivan Enderlin 2a912f7422 Merge pull request #1817 from matrix-org/revert-1779-af/nodejs-musl-gcc
Revert "Install musl-gcc for linux-musl nodejs releases"
2023-04-24 19:59:23 +02:00
Ivan Enderlin cfc6ec2c0d Revert "Install musl-gcc for linux-musl nodejs releases" 2023-04-24 19:59:01 +02:00
Ivan Enderlin 7e243c5a5b Merge branch 'main' into fix-issue-1728 2023-04-24 19:46:56 +02:00
Kévin Commaille 910c35b699 sled: Return all user IDs in get_user_ids
It would only return joined and invited user IDs,
since those are the only ones that were stored.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-24 18:54:29 +02:00
Kévin Commaille 3f1a596c8c indexeddb: Return all user IDs in get_user_ids
It would only return joined and invited user IDs,
since those are the only ones that were stored.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-24 18:54:29 +02:00
Kévin Commaille 493db9dd3f sdk-test: Add room member ban sync event
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-24 18:54:29 +02:00
Jonas Platte e5f4bbdc47 Upgrade dependencies 2023-04-24 13:59:01 +02:00
Jonas Platte e15e21a3d7 ffi: Use UniFFI proc-macros for SessionVerificationController type 2023-04-24 12:42:27 +02:00
Jonas Platte 3954cab2ab ffi: Use UniFFI proc-macros for Room type 2023-04-24 12:42:27 +02:00
Jonas Platte e1a727a27f ffi: Use UniFFI proc-macros for some misellaneousc symbols 2023-04-24 12:42:27 +02:00
Jonas Platte 75e7289ede ffi: Use UniFFI proc-macros for last parts of the Client type 2023-04-24 12:42:27 +02:00
Jonas Platte 00b3057e06 ffi: Use UniFFI proc-macros for ClientBuilder::build 2023-04-24 12:42:27 +02:00
Jonas Platte 5f802c1348 ffi: Use UniFFI proc-macros as much as possible for sliding sync 2023-04-24 12:42:27 +02:00
Jonas Platte 7ab14d9428 Enable Ruma's compat-user-id feature 2023-04-24 12:42:03 +02:00
Jonas Platte d527dd37b1 Upgrade Ruma 2023-04-24 12:42:03 +02:00
Jonas Platte 24a903ccb0 Fix Cargo.lock 2023-04-24 11:17:05 +02:00
Ivan Enderlin 8aad30ea4f Merge pull request #1779 from matrix-org/af/nodejs-musl-gcc
Install musl-gcc for linux-musl nodejs releases
2023-04-24 10:51:00 +02:00
Jonas Platte c6f491861e crypto-ffi: Use proc-macros for types no longer referenced in UDL 2023-04-24 10:31:12 +02:00
Jonas Platte 055f0ff988 crypto-ffi: Use proc-macros for Verification type and methods 2023-04-24 10:31:12 +02:00
Jonas Platte 058d27e0a3 crypto-ffi: Use proc-macros for Sas methods 2023-04-24 10:31:12 +02:00
Jonas Platte cb216044e1 crypto-ffi: Use proc-macros for OlmMachine methods 2023-04-24 10:31:12 +02:00
Jonas Platte 693ced4f4d crypto-ffi: Remove unused function 2023-04-24 10:31:12 +02:00
Jonas Platte 27cf710009 crypto-ffi: Use proc-macros for VerificationRequest methods 2023-04-24 10:31:12 +02:00
Jonas Platte 5202e3e1b9 crypto-ffi: Use proc-macros for QrCode methods 2023-04-24 10:31:12 +02:00
Jonas Platte d90d7b519b crypto-ffi: Use proc-macros for types not referenced in UDL 2023-04-24 10:31:12 +02:00
Jonas Platte d15447a2c1 crypto-ffi: Use proc-macro for exporting free functions 2023-04-24 10:31:12 +02:00
Ivan Enderlin be7b79b4e5 feat(ffi): Add binding for SlidingSyncBuilder::bump_event_types.
This patch adds a new binding on `SlidingSyncBuilder` to
`matrix_sdk::SlidingSyncBuilder::bump_event_types`.
2023-04-24 10:05:00 +02:00
Ivan Enderlin 7b2b3fa78f feat(sdk): Add support for bump_event_types in SlidingSync.
The `SlidingSyncBuilder` now has a new method: `bump_event_types`, to
configure the `bump_event_types` HTTP request parameter. This value
is passed to `SlidingSync`, which is used to build the `SlidingSync`
request.
2023-04-24 10:05:00 +02:00
Ivan Enderlin 1c2e7d8c0d chore: Update Cargo.lock. 2023-04-24 09:56:55 +02:00
Valere f8e4e3d7d5 fix(bindings): Withheld code mapping 2023-04-22 21:10:56 +02:00
Jonas Platte 9ffcb8bc8a ffi: Move error type into its own module 2023-04-20 18:24:47 +02:00
Jonas Platte 4f316a130f Remove uniffi_types modules 2023-04-20 18:24:47 +02:00
Jonas Platte 7e58e72671 Upgrade UniFFI 2023-04-20 18:24:47 +02:00
Florian Duros fd739a3676 matrix-sdk-crypto-js v0.1.0-alpha.7 2023-04-20 18:24:36 +02:00
Jonas Platte 167d81e36a ci: Simplify test-all-crates job 2023-04-20 14:52:34 +02:00
Richard van der Hoff 0a0da040d2 account.rs: add some comments (#1799) 2023-04-20 13:01:44 +01:00
Jonas Platte f2d2a20987 ffi: Change make_span to be a constructor, write docs 2023-04-20 11:43:45 +02:00
Jonas Platte 29068265db ffi: Add filtering of tracing events and spans 2023-04-20 11:43:45 +02:00
Jonas Platte 50f29e5a11 ffi: Add Span::is_none 2023-04-20 11:43:45 +02:00
Jonas Platte 54bd26d683 ffi: Add Span::current 2023-04-20 11:43:45 +02:00
Jonas Platte b532d35d21 Remove root_span from Client 2023-04-20 11:43:45 +02:00
Jonas Platte bb05ac7dac ffi: Add basic tracing bindings 2023-04-20 11:43:45 +02:00
Richard van der Hoff 0da8e56a68 crypto-js: wait for device updates in getUserDevices (#1790)
Wait for up to a second for any in-flight device list updates to complete.
2023-04-20 08:58:26 +01:00
Richard van der Hoff a88f53ee85 crypto-js: Expose more data on Device (#1786)
Implement Device.algorithms and Device.isSignedByOwner for the js bindings.
2023-04-20 08:57:54 +01:00
Jonas Platte 530fceb40d Fix some rustdoc warnings 2023-04-20 09:02:28 +02:00
Jonas Platte 7d5908beef sdk: Add more data to RemoteEventTimelineItem Debug string 2023-04-20 09:02:09 +02:00
Anderas 5d94a5d2f4 Update iOS Crypto SDK docs
… and add local podspec.
2023-04-19 18:09:21 +02:00
Jonas Platte 0e08d0f9ef sdk: Make EventTimelineItem's fields pub(super)
… and avoid an unnecessary clone.
2023-04-19 14:17:16 +02:00
Jonas Platte 3f6c9956ab sdk: Move timestamp to EventTimelineItem
… from Local/RemoteEventTimelineItem.
2023-04-19 14:17:16 +02:00
Jonas Platte c97d8afa58 sdk: Remove extra visibility boundary on {Local,Remote}EventTimelineItem 2023-04-19 14:17:16 +02:00
Jonas Platte abd508676e sdk: Remove {Local,Remote}EventTimelineItem from public API 2023-04-19 14:17:16 +02:00
Jonas Platte 7376198dfa sdk: Move sender, sender_profile, content to EventTimelineItem
… from Local/RemoteEventTimelineItem.
2023-04-19 14:17:16 +02:00
Jonas Platte 2960aafe3d sdk: Make EventTimelineItem an opaque struct instead of an enum 2023-04-19 14:17:16 +02:00
Jonas Platte 4daf31f5b1 Fix clippy lint 2023-04-19 14:17:16 +02:00
Jonas Platte bf4349ffb3 base-sdk: Don't separate member events from other state events
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-19 14:04:53 +02:00
Kévin Commaille b87a4e8c20 base-sdk: Don't separate member events from other state events
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-19 13:16:55 +02:00
Alfonso Grillo 3b1ed1403d sdk: Add DM invitation to m.direct account data upon accepting it 2023-04-18 18:05:48 +02:00
Alfonso Grillo fe10bcc814 base: Use member event is_direct field for is_direct() on invited rooms 2023-04-18 18:05:48 +02:00
Jonas Platte be67c91a6b Use regular backticks in matrix-sdk-ffi/README.md 2023-04-18 15:20:51 +02:00
Jonas Platte 89c06568dd Remove unneeded explicit lifetime 2023-04-18 15:20:51 +02:00
Alfonso Grillo dffa1b16c3 sdk: Skip user's own ID when marking a room as DM 2023-04-18 13:41:34 +02:00
Jonas Platte 5bc20669c2 sdk: Implement MSC3925 for the timeline
https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3925-replace-aggregation-with-full-event.md
Implemented in Synapse 1.79.0:
https://github.com/matrix-org/synapse/releases/tag/v1.79.0
2023-04-18 13:14:33 +02:00
Jonas Platte 86930581a5 Upgrade Ruma 2023-04-18 13:14:33 +02:00
Jonas Platte 40c1252f48 sdk: Simplify handling of bundled relations in timeline 2023-04-18 13:14:33 +02:00
Alfonso Grillo cfdc9b2a82 Expose accept_invitation to UniFFI 2023-04-18 12:45:30 +02:00
Jorge Martin Espinosa c5919e3e63 Add workaround for building Android bindings with supported NDK versions (r23+) 2023-04-17 16:06:46 +00:00
Andrew Ferrazzutti 4fbdd70a53 Use local reference to workflow file 2023-04-14 20:20:59 +09:00
Andrew Ferrazzutti a78b260857 Install musl-gcc for linux-musl nodejs releases 2023-04-14 17:43:42 +09: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ć 8137c39f3a Don't log the raw Ruma sliding sync response 2023-04-13 16:14:47 +02:00
Jonas Platte f21946ef06 sdk: Avoid raw JSON in debug strings 2023-04-13 16:00:50 +02:00
Damir Jelić 730b66e26c Add withheld code support 2023-04-13 11:46:19 +02:00
Damir Jelić 316b29c95f Merge branch 'main' into valere/msc_2399 2023-04-13 10:59:05 +02:00
Jonas Platte 1e737208e5 sdk: Handle ephemeral events after timeline events 2023-04-12 16:15:35 +02:00
Jonas Platte 36b9064e51 sdk: Reduce indentation in read receipt handling code
… by factoring out a function.
2023-04-12 15:24:52 +02:00
Jonas Platte 086106a96d sdk: Simplify return type of fetch_in_reply_to_details 2023-04-12 13:16:36 +02:00
Simon Farre b9fba69dc7 ffi: Fix kotlin build failure
Workaround for https://github.com/mozilla/uniffi-rs/issues/1434
2023-04-12 10:11:07 +00:00
Simon Farre 8bad59bbe1 ffi: Add avatar_url getter for SlidingSyncRoom
Signed-off-by: Simon Farre <simon.farre.cx@gmail.com>
2023-04-11 15:47:40 +00:00
Damir Jelić 972d5cefdb If we can't load an outbound group session from the store, rotate it
An error is currently thrown if loading an outbound group session fails.
This error may only affect loading outbound group sessions, while other
store operations continue to work fine. As a result, the user is unable
to send messages, but can still use the application without any problems.

Since outbound group sessions are rotated frequently, we can simply
rotate the session if loading fails. However, if the error is related to
a more serious storage issue, persisting the newly rotated outbound group
session will also fail. If the error is specific to outbound group
sessions, the user will be able to continue using the application without
interruption.
2023-04-11 10:08:06 +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
Kévin Commaille a1cfb4bcf1 sdk: Re-export CrossSigningStatus
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-04-08 21:47:10 +02:00
Ivan Enderlin f6fb082dc9 chore(sdk): Remove unused methods in sliding_sync
chore(sdk): Remove unused methods in `sliding_sync`
2023-04-06 18:02:08 +02:00
Ivan Enderlin df9f48dc38 chore(sdk): Replace to_string by to_owned. 2023-04-06 18:01:45 +02:00
Ivan Enderlin 0b4c94961d test(sdk): Fix a test. 2023-04-06 17:42:28 +02:00
Ivan Enderlin 41d44f0d49 chore(sdk): Remove unused methods.
Those methods are public, but never used by our current users.

Moreover, there is some big overlaps. For example, `storage_key`,
`cold_cache` and `no_cache` do the same thing: They update the
`storage_key` field. Or `add_fullsync_list` is just a helper that is
never used except in the tests etc.
2023-04-06 16:29:38 +02:00
Jonas Platte 29255781cc Fix comments in Cargo.toml 2023-04-06 15:49:09 +02:00
Ivan Enderlin 1756aabe35 fix(sdk): SlidingSyncList.room_list emits updates for non-moving rooms
fix(sdk): `SlidingSyncList.room_list` emits updates for non-moving rooms
2023-04-06 15:08:47 +02:00
Ivan Enderlin 4ee4906536 chore(sdk): Make Clippy happy. 2023-04-06 11:36:37 +02:00
Ivan Enderlin fbe162a1bc test(sdk): Fix an integration test in Sliding Sync.
The test does the following:

1. Create 2 (identical) lists,
2. Do a sync.
3. Assert that the 1st and 2nd lists are receiving an update,
4. Add a 3rd (identical) list,
5. Do a new sync,
6. Assert that 3rd list is receiving an update.

This last step is wrong. All lists should receive an update as they
are identical.
2023-04-06 11:29:10 +02:00
Damir Jelić cebeca85d0 fixup! Minor fixes to the withheld handling 2023-04-06 11:23:27 +02:00
Damir Jelić 8c23b6e7b2 Minor fixes to the withheld handling 2023-04-06 11:18:14 +02:00
Ivan Enderlin eb0e97b902 test(sdk): Test that SlidingSyncList.room_list receives “diff”.
This patch updates a test to ensure that `room_list` receives “diff”
for rooms that are modified by a sync operations, but also by another
updates (like a new event).
2023-04-06 11:14:09 +02:00
Ivan Enderlin 828e36e3af test(sdk): Test apply_sync_operations removes rooms that have been updated. 2023-04-06 11:14:09 +02:00
Ivan Enderlin d68a22c378 fix(sdk): SlidingSyncList.room_list emits updates for non-moving room.
The `SlidingSyncList.room_list` field is used to store the list of rooms
for a particular Sliding Sync list. It's used by the user to receive
“diff”s when a room sees its position modified. For example when a new
room receives a message, it “climbs back up” the entire room list to
be at the top place. Another example is when a new room is created,
some rooms will move around to give the new room a space. All those
moves will create “diff”.

However, the user also expects to receive a “diff” when a room has
received some updates, even if it's position doesn't change. For
example, when a room is already at the top of the list but receives a
new message: It has received an update, but its position stays the same.

This specific latter feature was implemented before, but it has been
removed by accident in https://github.com/matrix-org/matrix-rust-sdk/
pull/1699 (more specifically in https://github.com/matrix-org/matrix-
rust-sdk/pull/1699/commits/861a05be69a566d9a4ad125dc6ecb418d2b3210f).
At that time, it was not clear why the code was filtering for specific
filled room entires, to set the filled room entries, at the same
position. Zero comment, zero test. I didn't consider this as a feature
but as a bug.

So this patch re-introduces this feature. Hopefully in a more optimal
way. If a room has already triggered a first “diff” because of a
position change, it won't trigger a second “diff” because it has
received an update (which was the case before).

The `SlidingSyncList::handle_response` method has also been renamed
`update`, as it does update, whenever it comes from.

The code has been commented and documented to explain this feature.

Existing tests have been updated, especially for `apply_sync_operations`
which now ensure the `rooms_that_have_received_an_update` collections is
updated accordingly. Another test has been updated specifically to test
the “diff”s received by `room_list`.
2023-04-06 11:09:55 +02:00
Ivan Enderlin a71970d3de chore(sdk): Rephrase a panic message. 2023-04-06 10:50:20 +02:00
Ivan Enderlin c07b060080 feat(sdk): Create vectors with correct capacity to avoid reallocations.
`updated_rooms` and `updated_lists` can be created with an initial
capacity. Doing so will avoid reallocations if the vector is too small.
2023-04-06 10:49:15 +02:00
Jonas Platte b033508e9b Fix clippy lints 2023-04-06 09:57:37 +02:00
Damir Jelić ba95a7bfd8 Use BTreeMap instead of HashMap
We use BTreeMap everywhere else, so let's be consistent with that
choice.
2023-04-05 14:38:25 +02:00
Damir Jelić 64d76308c6 Use as_ref() instead of stars and ampersands 2023-04-05 14:29:49 +02:00
Damir Jelić 52fef9f373 Don't use a Result return type in the withheld handling method 2023-04-05 14:27:00 +02:00
Damir Jelić c3437ac4cf Apply suggestions from code review
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2023-04-05 14:22:19 +02:00
Damir Jelić 6fd129dcfa Remove some commented out code and fix some formatting 2023-04-05 13:22:41 +02:00
Damir Jelić 1f8c67bbb2 Use the atomic bool seralizer for the no_olm flag 2023-04-05 13:15:51 +02:00
Damir Jelić 82a4b53cf9 fixup! Apply suggestions from code review 2023-04-05 13:15:38 +02:00
Damir Jelić 673db38a72 Apply suggestions from code review
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2023-04-05 13:14:07 +02:00
Damir Jelić 50d477b91c More clippy fixes 2023-04-05 11:54:29 +02:00
Damir Jelić 1c2964f3f3 Update crates/matrix-sdk-crypto/src/types/events/room_key_withheld.rs
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2023-04-05 11:52:25 +02:00
Damir Jelić 92202e20e7 Fix some clippy warnings 2023-04-05 11:45:50 +02:00
Damir Jelić c032609f4d Fix the js tests now that we bumped our DB version 2023-04-05 11:34:28 +02:00
Olivier Wilkinson (reivilibre) 9c54de1817 Downgrade memory store 'Saved changes in <time>' to debug
It doesn't seem very interesting since a memory store will be fast anyway. In practice, this fills the console quickly.
2023-04-05 11:31:37 +02:00
Damir Jelić d606bd3e9f Document the new withheld content enums 2023-04-05 11:18:50 +02:00
Ivan Enderlin 7c578dccb9 fix(sdk): SlidingSync uses the same storage keys for storing *and* restoring
fix(sdk): `SlidingSync` uses the same storage keys for storing *and* restoring
2023-04-05 11:11:47 +02:00
Ivan Enderlin 5563213106 chore(test): Make Clippy happy. 2023-04-05 10:56:08 +02:00
Jonas Platte 91dc87808d ffi: Add InReplyToDetails 2023-04-05 10:50:08 +02:00
Jonas Platte 1217067f74 ffi: Rename ProfileTimelineDetails to ProfileDetails 2023-04-05 10:50:08 +02:00
Jonas Platte 5b512f020e sdk: Rename details field to event in InReplyToDetails 2023-04-05 10:50:08 +02:00
Ivan Enderlin 042d6bab72 test(sdk): Test SlidingSync is store and restored correctly. 2023-04-05 10:06:59 +02:00
Jonas Platte ef3ffda2d3 ffi: Replace EventTimelineItem::{raw, fmt_debug} by debug_info 2023-04-05 09:56:00 +02:00
Jonas Platte 3ac6b10daa sdk: Add RemoteEventTimelineItem::latest_edit_json
… and rename raw to original_json to disambiguate.
2023-04-05 09:56:00 +02:00
Ivan Enderlin be631849e8 fix(sdk): Move SlidingSync::cache_to_storage to cache.
This patch also re-uses the `format_storage_key_*` functions to use the
same key as the `restore_sliding_sync_state` function. It fixes a bug
where the keys were mismatching.
2023-04-05 09:17:54 +02:00
Ivan Enderlin fc8b9d7de4 feat(sdk): Extract SlidingSync cache functions into their own module. 2023-04-05 09:05:03 +02:00
Jonas Platte 2c38c6c371 sdk: Log in_reply_to field in timeline::Message Debug impl 2023-04-04 18:09:08 +02:00
Damir Jelić 81e2725b6f Remove DirectWithheldCode 2023-04-04 13:30:29 +02:00
Simon Farre fc56ae3dcf Expose invite_user_by_id over the FFI
This PR is self explanatory.

Exposes said function to the FFI. Defined in the .adl file,
and defined to throw `ClientError`

Signed-off-by: Simon Farre <simon.farre.cx@gmail.com>
Co-authored-by: Damir Jelić <poljar@termina.org.uk>
2023-04-04 09:04:54 +00:00
Chris Smith e8c56f8163 feat: expose inviter to ffi
This allows us to know who has invited us to rooms
2023-04-04 10:29:04 +02:00
Ivan Enderlin 690c3b1977 Merge pull request #1699 from Hywan/feat-sdk-sliding-sync-list-revamp-sync-mode
feat(sdk) Revamp Sliding Sync List state lifetime
2023-04-03 15:04:21 +02:00
Ivan Enderlin ac800180a7 Merge branch 'main' into feat-sdk-sliding-sync-list-revamp-sync-mode 2023-04-03 14:36:26 +02:00
Damir Jelić b7a78c865c Ensure correct Olm session selection for encrypted messages
In some cases, restoring client state from backups may cause Olm sessions
to become corrupted, resulting in a backward ratchet. As a consequence,
the receiving side is unable to decrypt messages. To address this issue,
the failed side initiates a new Olm session and sends a dummy encrypted
message.

However, this dummy message also creates a new Olm session on the
sender's side. To ensure that both sides use the same new session, we
must sort sessions by their creation timestamp before encrypting new
messages.

Although the session list was sorted correctly previously, we selected
the wrong side of the list, resulting in an outdated session being
selected instead of the newest one. This patch resolves the issue by
selecting the newest Olm session and adds a test to the session getter
logic to prevent reintroduction of this bug.
2023-04-03 13:03:09 +02:00
Ivan Enderlin aeda2d363c Merge pull request #1736 from Hywan/feat-sliding-sync-storage 2023-04-03 12:54:49 +02:00
Ivan Enderlin 69af5b3c6f feat(sdk): When a SlidingSync cache is obsolete, let's remove it.
Prior to this patch, when `SlidingSync` was built with a storage key,
the storage was read to find a previous `SlidingSync` version that
was in a cache. To put a `SlidingSync` instance in the cache, it
was serialized to JSON. When one reads from the cache, the value is
deserialized. A problem happens when the internal representation of
`SlidingSync` and other types (e.g. `SlidingSyncList`) have changed (due
to an SDK update for example): Loading a previous state from the cache
results in an error.

After discussion with the teams, clients don't want to deal with
obsolete cache values. In such a scenario, (i) they cannot interact
with the cache to remove the obsolete entry, and (ii) their only
possibility is to log out and log in the user again. What an unfriendly
user experience!

This patch modifies this behaviour. When loading a `SlidingSync` type or
a `SlidingSyncList` type from the cache, 3 cases are now handled:

1. The cache entry exists and has been successfully deserialized: in
   this case, we do our business.
2. The cache entry exists, but it wasn't possible to deserialize it: the
   cache entry is declared as obsolete, and the entire `SlidingSync`
   cache is cleaned up, so all entries for `SlidingSync` and
   `SlidingSyncList` are removed from the storage,
3. The cache entry doesn't exist: we do nothing particular.

Note that if one cache is obsolete, all cache entries are removed.
2023-04-03 12:10:50 +02:00
Ivan Enderlin 56a7854b53 Merge branch 'main' into feat-sdk-sliding-sync-list-revamp-sync-mode 2023-04-03 10:10:31 +02:00
Ivan Enderlin 60d4ac14b7 Merge pull request #1725 from Hywan/feat-remove-jack-in
feat(labs): Remove `jack-in`
2023-04-01 13:27:20 +02:00
Richard van der Hoff 78b9758377 Merge pull request #1721 from matrix-org/rav/crypto-js-releases
Fixes and documentation for crypto-js releases
2023-03-31 11:22:50 +01:00
Richard van der Hoff a5fd71a251 prettify markdown 2023-03-31 11:08:26 +01:00
Alfonso Grillo 7958a20fc1 Add short_retry on get_profile 2023-03-30 22:00:09 +02:00
Jonas Platte 6878f52885 Upgrade UniFFI 2023-03-30 21:53:02 +02:00
Ivan Enderlin ea5b4c98c1 feat(labs): Remove jack-in.
This was a fun ride, but nobody uses it, and we don't need it anymore.

Thanks for all the fishes.
2023-03-30 18:03:38 +02:00
Ivan Enderlin 60432abe0d feat(ffi): Add SlidingSync::reset_lists. 2023-03-30 18:01:17 +02:00
Ivan Enderlin 2d537e5211 feat(sdk): Add SlidingSync::reset_lists.
The `SlidingSync::stream` method no longer resets the lists everytime
it is called. If one wants to reset the lists, they need to call
`SlidingSync::reset_lists`.
2023-03-30 18:01:16 +02:00
Jonas Platte 7dd086fcdc Make sqlite bundling optional 2023-03-30 17:59:51 +02:00
Ivan Enderlin bd6075f6b4 fix(sdk): Try to find workarounds about the bug in SlidingSync Proxy.
It still about https://github.com/matrix-org/sliding-sync/issues/52. We
try to rebuilt a valid `range`. Hopefully, the bug will be fixed soon,
and we will be able to clean that temporary code.
2023-03-30 17:52:33 +02:00
Kévin Commaille 48b67759ed sdk: Make PaginationOption's custom variant's strategy return a ControlFlow
It is semantically more correct than an Option.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-30 14:39:08 +00:00
Richard van der Hoff f60c678af8 Apply suggestions from code review
Co-authored-by: Damir Jelić <poljar@termina.org.uk>
2023-03-30 15:38:42 +01:00
innocent fish a35d96c15e Update swift.rs 2023-03-30 16:08:06 +02:00
Alfonso Grillo ade7fabb6b Add Client::get_profile 2023-03-30 15:44:27 +02:00
Ivan Enderlin dd4cb9c793 test(sdk): Fix SlidingSync doctests. 2023-03-30 15:10:17 +02:00
Ivan Enderlin 2ace220366 chore(sdk): Rename rooms_list to room_list. 2023-03-30 15:06:19 +02:00
Richard van der Hoff 4f3f9c7af8 Fixes and documentation for crypto-js releases
The tags are supposed *not* to contain `v`, for consistency with the other
crates.
2023-03-30 13:57:15 +01:00
Richard van der Hoff 43d4e7b46a Merge pull request #1719 from matrix-org/release-matrix-sdk-crypto-js-0.1.0-alpha.6
matrix-sdk-crypto-js v0.1.0-alpha.6
2023-03-30 13:53:39 +01:00
Ivan Enderlin 4be02d0e99 test(sdk): Wait longer on the server to reply. 2023-03-30 14:43:33 +02:00
Ivan Enderlin e34708862d test(sdk): Remove a useless SS test. 2023-03-30 14:31:52 +02:00
Ivan Enderlin f269202ece fix(sdk): Try to find a workaround for a bug in the SS Proxy.
See https://github.com/matrix-org/sliding-sync/issues/52 for the
explanation. And this patch has comments explaining how I try to work
around this.
2023-03-30 14:23:59 +02:00
Kévin Commaille 408f966a50 sdk: Rename flag to update fully-read marker
Its meaning has changed since it is used in more cases.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-30 14:23:57 +02:00
Kévin Commaille 553ffe97a8 sdk: Never insert read marker in last position in timeline
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-30 14:23:57 +02:00
Ivan Enderlin 23f16f065b fix(sdk): Make out-of-bounds DELETE a no-op. 2023-03-30 13:37:31 +02:00
Richard van der Hoff f246296ff9 matrix-sdk-crypto-js v0.1.0-alpha.6
Release `crypto-js` / Publish 🕸 [m]-crypto-js (push) Failing after 34s
2023-03-30 12:26:29 +01:00
Mauro ca6faffc72 bindings: Reset the timeline when the user's ignore list is updated 2023-03-30 11:22:06 +00:00
Ivan Enderlin 4c42205633 chore(labs): Update to the latest SS version. 2023-03-30 12:57:24 +02:00
Richard van der Hoff f9881065c1 Add an API to notify apps when a megolm key is received
This is useful for applications which want to have another go at decryption
when some room keys arrive.
2023-03-30 12:29:39 +02:00
Ivan Enderlin 1af1504ef3 feat(ffi): Update to the latest Sliding Sync version. 2023-03-30 11:42:22 +02:00
Damir Jelić 7fd1c93e1a Document the fact that we require protoc to build the bindings 2023-03-30 11:06:15 +02:00
Doug a33d2ad28f bindings: Add ignore_user 2023-03-30 08:59:19 +00:00
Ivan Enderlin bd8a97cfd7 feat(sdk): Update the SyncOp::Delete operation.
It seems that the `DELETE` operation actually deletes an entry. So
let's update the code to reflect that :-).
2023-03-30 10:35:27 +02:00
Ivan Enderlin ce94fcc2e2 feat(sdk): Update the SyncOp::Insert operation.
It seems that the `INSERT` operation actually inserts a new entry. So
let's update the code to reflect that :-).
2023-03-30 10:00:21 +02:00
Ivan Enderlin a442ca1893 Merge branch 'main' into feat-sdk-sliding-sync-list-revamp-sync-mode 2023-03-29 17:29:15 +02:00
Ivan Enderlin db7746062f doc(sdk): Write more doc. 2023-03-29 17:23:37 +02:00
Ivan Enderlin bc9d5016dc chore(sdk): Move maximum_number_of_rooms's update a little bit above. 2023-03-29 17:15:07 +02:00
Ivan Enderlin 581c02307e fix(sdk): Rewrite SyncOp::Invalidate entirely.
`SyncOp::Invalidate` means _invalidating_ a particular range. When a
room is `Filled`, it becomes `Invalidated`, when it is `Invalidated` it
stays `Invalidated`, and when it is `Empty` it stays `Empty`.

Before this patch, `Empty` was becoming `Invalidated`, which apparently
is a bug.

This patch also fixes out-of-bound accesses, and adds many tests.

Finally, this patch renames `update_state` to `update_room_lists`.
2023-03-29 17:11:01 +02:00
Ivan Enderlin a4f8c35efc fix(sdk): Rewrite SyncOp::Insert entirely.
`SyncOp::Insert` means _inserting_ a new room ID. The `rooms_list`
contains all possible rooms (based on `maximum_number_of_rooms`, a value
returned by the server). Here, inserting = setting
`RoomListEntry::Filled` at a particular index of `rooms_list`, that's
it.

The previous code was doing very complex stuff, like removing things
around the `index` if something etc. It was using the requested ranges
(the range passed to the request) etc.

Applying `SyncOp` should be simple and is focused on updating
`rooms_list` only: the requested ranges have nothing to do here.

This patch also prevents against out-of-bounds acccesses, which wasn't
the case before.
2023-03-29 16:30:06 +02:00
Ivan Enderlin 2878148b0e fix(sdk): Prevent ouf of bounds accesses for SyncOp::Delete.
This patch prevents out of bounds acceses for `SyncOp::Delete`, and adds
more tests.

This patch also removes a cast from `UInt` to `u32` to `usize`. It's now
from `UInt` to `usize` directly.
2023-03-29 16:10:44 +02:00
Ivan Enderlin d6cfd871c4 feat(sdk): Protect against invalid responses from the server. 2023-03-29 15:55:02 +02:00
Ivan Enderlin 18597f8f63 fix(sdk): Fix out-of-bounds and re-implement SlidingOp::Sync handler.
First off, this patch renames `ops` to `sync_operations` and `room_ops`
to `apply_sync_operations`.

Second, the `SlidingOp::Sync` was creating an out-of-bounds access
depending of the range present in the server's response. For example, if
the `rooms_list` contains 5 elements (because the
`maximum_number_of_rooms` is set to 5), and the server replies with:

```json
{
    "op": "SYNC",
    "ranges": [3, 17],
    "room_ids": […]
}
```

the previous code was setting a new `RoomListEntry` at indices `3..=17`,
whilst the `rooms_list` contains only indices from `0..=4`. That's
annoying.

The previous code was also counting the number of `room_ids` for
nothing, just to execute the iterator that was applying the actual
changes in a `map`. Well, everything was fishy.

This patch updates the code to protect against an unexpected server's
reply by raising an `Err`. This patch also adds tests.
2023-03-29 15:30:10 +02:00
Jonas Platte 6b5f0b8ec3 Reset visible timeline when a gappy sync arrives 2023-03-29 14:00:33 +02:00
Damir Jelić 172867fd4d Simplify the withheld code receiving 2023-03-29 13:44:28 +02:00
Jonas Platte 50cc356c7b Use Observable for sliding_sync_reset_broadcast_tx 2023-03-29 13:23:24 +02:00
Jonas Platte 2ac192f307 Use str::to_owned instead of str::to_string
Expresses intent more clearly.
2023-03-29 13:23:24 +02:00
Damir Jelić 03aba95e1e Switch the FFI bindings to use the SQLite cryptostore 2023-03-29 11:44:33 +02:00
Ivan Enderlin 616e57eb1c test(sdk): Update integration test suite for SS. 2023-03-29 11:05:02 +02:00
Ivan Enderlin 5c695bbf8f feat(ffi): Update add_range calling. 2023-03-29 10:55:14 +02:00
Ivan Enderlin 861a05be69 feat(sdk): Remove updated_rooms from SlidingSyncListInner::update_state.
The `updated_rooms` argument was passed to `find_rooms_in_list` to
update the `room_list`: the update is setting the filtered room list
entry to `RoomListEntry::Filled`.

_But_, `find_rooms_in_list` was already filtering rooms which are
`Filled`. So it does… nothing: it filters rooms which are `Filled` to
update them to `Filled`.

So we can remove `find_rooms_in_list` because it becomes useless. And we
can remove `updated_rooms` too.

The `rooms_list` is updated by `rooms_ops` itself. Let's keep
modifications in one unique place.
2023-03-29 10:51:33 +02:00
Damir Jelić cf17a05ecc Restructure the withheld code types 2023-03-29 10:45:26 +02:00
Ivan Enderlin 385dd9113b feat(sdk): Simplify SlidingSyncListInner::update_state.
The `update_state` method of `SlidingSyncListInner` has basically
2 cases:

1. For an initial response,
2. For other responses.

The code between the 2 cases were almost identical. Or, they could be
identical. The few exceptions are:

* In the first case, the `rooms_list` updates were taking the
  form of a `VectorDiff::Append`, while the second case, it was a
  `VectorDiff::PushBack`.
* In the first case, the `is_cold` flag was set to `false`.

It's fine for the clients to receive only a `VectorDiff::Append` event
only. So let's make it uniform.

And it appears that the `is_cold` field is now private, and never read
anywhere else. So… it's… basically useless. We can remove it! It was
previously used here to know which flow to use, but since we can make
both flows identical, its role becomes insignificant.
2023-03-29 09:52:58 +02:00
Ivan Enderlin 3f4f9c1fbd chore(sdk): Remove a useless reference. 2023-03-29 09:52:05 +02:00
Ivan Enderlin bf163ef5ad feat(sdk): Replace checked_sub + unwrap_or_default by saturating_sub. 2023-03-29 09:23:47 +02:00
Ivan Enderlin 83cf820508 chore(sdk): More code in blocks to remove mutable variables with larger scopes. 2023-03-29 09:23:10 +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
Jonas Platte 9bfd88cec4 sdk: Always use tokio OnceCell
Tokio's sync module works on wasm as well.
2023-03-28 21:08:57 +02:00
Damir Jelić d5fba19655 Don't allow the no_olm withheld code sent flag to be reset 2023-03-28 14:31:55 +02:00
Ivan Enderlin ab29e1b319 !debug 2023-03-27 14:32:11 +02:00
Ivan Enderlin a2dbeda758 feat(sdk): SlidingSyncList cannot be removed from SlidingSync.
`create_range` and `SlidingSyncList::next_request` return a `Result`
instead of an `Option`. It was a legacy from the old `impl Iterator` of
`SlidingSyncListRequestGenerator`. But actually, when `create_range`
fails, it must return a `Err` value, not a `None` value.

And since it's a regular error, `sync_once` can propagate the
error. Thus, it is no longer necessary to “update” the list of
`SlidingSyncList`. It's not necessary to remove some items in this list
when the `next_request` method can no return a value: it always returns
a value, except when a error happens.
2023-03-27 14:25:40 +02:00
Damir Jelić 0d3fd31893 Clean up the withheld code types 2023-03-27 09:21:48 +02:00
dependabot[bot] 8b5de47acb chore(deps): bump openssl from 0.10.45 to 0.10.48 (#1706)
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.45 to 0.10.48.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.45...openssl-v0.10.48)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-25 21:12:52 +01:00
Kévin Commaille 5a7ea607c6 sdk: Use new filter constructor to enable room members lazy-loading
* sdk: Use new filter constructor to enable room members lazy-loading

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-25 21:12:09 +01:00
Flescio 09fc258f9a ffi: Make name parameter optional in CreateRoomParameters 2023-03-23 16:56:52 +00:00
Ivan Enderlin fdba90587a feat(sdk): Reset the SlidingSyncList when starting a stream. 2023-03-23 17:44:25 +01:00
Ivan Enderlin 63ed675158 doc(sdk): Extract the giant SS doc inside a README.md file.
Why? Because it takes forever to scroll to the code when opening
`sliding_sync/mod.rs` :-p.
2023-03-23 17:38:20 +01:00
Ivan Enderlin 767e9b3cf2 feat(sdk) Reset SlidingSyncList when a range is modified. 2023-03-23 16:32:53 +01:00
Ivan Enderlin 8d4ceac21f test(sdk): Continue to fix, test and clean up SlidingSyncList
test(sdk): Continue to fix, test and clean up `SlidingSyncList`
2023-03-23 13:18:09 +01:00
Ivan Enderlin d1616d5654 chore(sdk): Remove a useless comma. 2023-03-23 13:17:54 +01:00
Ivan Enderlin 492e08598c feat(sdk): Revamp SlidingSyncList::*range*.
First off, `set_ranges`, `set_range`, `add_range` and `reset_ranges`
take a `&[(U, U)]` instead of a `Vec<(U, U)>` when a vector was needed,
or takes a `(U, U)` instead of 2 arguments when it was needed.

Second, all those methods now return a `Result<(), Error>`. The
`Error::CannotModifyRanges` is raised if the chosen `SlidingSyncMode`
doesn't allow to modify ranges. Basically, `Selective` does allow a
user to modufy the ranges, but there is no real ranges with `Growing` or
`Paging`. Let's make it an explicit error.

Finally, `SlidingSyncListInner` has 2 methods: `set_ranges` and
`add_range`, but without any “ranges check”; `SlidingSyncList` does call
the inner methods, but does the checks.
2023-03-23 12:34:48 +01:00
Ivan Enderlin 6716939b6d chore(sdk): Re-order fields in SlidingSyncListInner. 2023-03-23 11:56:08 +01:00
Ivan Enderlin 73959e023b feat(sdk): Remove for FullSync suffix to sync modes. 2023-03-23 11:53:39 +01:00
Ivan Enderlin ec02d462ea feat(sdk): The SlidingSyncList.full_sync_* fields are no longer useful.
They are unused. Let's remove them.
2023-03-23 11:50:38 +01:00
Ivan Enderlin 0eee472829 feat(sdk): Remove send_updates_for_items.
This field is always set to `true` by all the clients. So let's consider
that something we always want, and let's remove code complexity.
2023-03-23 11:46:56 +01:00
Ivan Enderlin 296328e8e0 feat(sdk): Don't rename SlidingSyncState's variants when serialization.
We are going to introduce a BC break. So let's take the opportunity to
get rid off of this renamings too.
2023-03-23 11:37:53 +01:00
Ivan Enderlin 831090c92c feat(sdk): Export RoomListEntry into its own module. 2023-03-23 11:24:30 +01:00
Ivan Enderlin fa361aea16 chore(sdk): Make Clippy happy. 2023-03-23 11:18:09 +01:00
Ivan Enderlin 91e9942fcd feat(sdk): Export FrozenSlidingSyncList into its own module. 2023-03-23 11:15:24 +01:00
Kévin Commaille 5eeea83c19 sdk: Get push actions of retried decrypted events in the timeline
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-23 10:59:55 +01:00
Kévin Commaille 98386cf527 sdk: Get push actions of decrypted events
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-23 10:59:55 +01:00
Kévin Commaille a3667a21c9 sdk: Simplify e2ee branching of Common::event
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-23 10:59:55 +01:00
Kévin Commaille fae10ae7f3 sdk: Add private method to get the current push rules
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-23 10:59:55 +01:00
Ivan Enderlin 1db8084c4f chore(sdk): Make Clippy happy. 2023-03-23 10:38:37 +01:00
Ivan Enderlin b544639187 chore(sdk): Re-ordering methods inside SlidingSyncList. 2023-03-23 10:07:19 +01:00
Ivan Enderlin 5e1fda9834 test(sdk): Update the assert_ranges macro to be able to define the first list state. 2023-03-23 10:05:34 +01:00
Ivan Enderlin 5bae1a2a4e chore(sdk): rooms_ops takes a &[(UInt, UInt)] for the ranges. 2023-03-22 16:59:45 +01:00
Ivan Enderlin defe48bf3c test(sdk): Test SlidingSyncList::get_room_id. 2023-03-22 16:45:53 +01:00
Ivan Enderlin caf55308a5 feat(sdk): Remove SlidingSyncList::rooms_updated_broadcast_stream.
This method is never used by the clients, so it's basically public dead
code. It doesn't fulfill a particular requirement nor a need, so let's
remove it.

Fixes https://github.com/matrix-org/matrix-rust-sdk/issues/1694.
2023-03-22 16:40:06 +01:00
Ivan Enderlin f2aebcb983 test(sdk): (Re)write a test for SlidingSyncListInnner::find_rooms_in_list. 2023-03-22 16:40:06 +01:00
Ivan Enderlin 2840242331 chore(sdk): Use &[…] instead of &Vec<…>.
One less pointer indirection.
2023-03-22 16:40:06 +01:00
Ivan Enderlin 69a779a7eb feat(sdk): Improve SlidingSyncListInner::find_rooms_in_list.
The `SlidingSyncListInner::find_rooms_in_list` method can be greatly
improved by using the `Iterator` API.

It was already using `Iterator::skip`, great! Let's continue by using
`Iterator::take` instead of using a stop index.

And let's use `Iterator::enumerate` to avoid using a mutable index.
2023-03-22 16:40:06 +01:00
Ivan Enderlin b7da197846 doc(sdk): Add missing documentation. 2023-03-22 16:40:06 +01:00
Ivan Enderlin 0b9b01727e feat(sdk): Remove find_rooms?_in_list from the public API.
`SlidingSyncList::find_room_in_list` and
`SlidingSyncList::find_rooms_in_list` aren't useful (and never used) by
the public consumer of this API. Let's remove it. They are only used for
internal purposes.
2023-03-22 16:40:06 +01:00
Ivan Enderlin 0d58695e39 feat(sdk): SlidingSyncListInner::update_request_generator_state returns a Result.
Before, the `update_request_generator_state` method was emitting an
`error!` log, but not an error. But that's clearly an error!

This patch updates the method to return a `Result<(), Error>`, and adds
a new variant to `Error`.
2023-03-22 16:40:06 +01:00
Ivan Enderlin f5ba7ddc4a fix(sdk): Prevent bugs, remove expensive clones, and simplify SlidingSyncList
fix(sdk): Prevent bugs, remove expensive clones, and simplify `SlidingSyncList`
2023-03-22 16:39:30 +01:00
Ivan Enderlin 7052e9ff64 doc(sdk): Fix typos. 2023-03-22 16:38:45 +01:00
Ivan Enderlin 8796bfe874 doc(sdk): Fix typos. 2023-03-22 16:16:33 +01:00
Ivan Enderlin 9e7c6c3632 feat(sdk): Remove deduplication logic from Sliding Sync
feat(sdk): Remove deduplication logic from Sliding Sync
2023-03-22 14:44:45 +01:00
Ivan Enderlin 6617e94a9d Merge pull request #1632 from Hywan/doc-sdk-sliding-sync
doc(sdk): Rephrase the Quick refreshing Section
2023-03-22 14:19:46 +01:00
Ivan Enderlin 7e8c8b1a14 chore(sdk): Make Clippy happy. 2023-03-22 13:31:48 +01:00
Ivan Enderlin 9078a30e28 chore(sdk): Move code around, and remove pub on fields of a private struct. 2023-03-22 13:17:52 +01:00
Ivan Enderlin 485ca402f4 test: Use available setter. 2023-03-22 13:17:52 +01:00
Ivan Enderlin d41293879a test(sdk): Move tests from list/request_generator.rs to list/mod.rs. 2023-03-22 13:17:52 +01:00
Ivan Enderlin d5babfbb88 test(sdk): Test SlidingSyncList::(set_)timeline_limit. 2023-03-22 13:17:52 +01:00
Ivan Enderlin b842b2f96d feat(sdk): Add getters and setters on SlidingSyncList. 2023-03-22 13:17:52 +01:00
Ivan Enderlin 32e83a942d fix(sdk): Prevent bugs, remove expensive clones, and simplify SlidingSyncList.
There are problems with `SlidingSyncListRequestGenerator`:

* It's part of the module API,
* It contains a clone of `SlidingSyncList`.

To create a `SlidingSyncListRequestGenerator`, one has to
call `SlidingSyncList::request_generator`. It was done in
`SlidingSync::stream`. The problem is that it clones `SlidingSyncList`.
So theoritically it is possible to create multiple request generators
for the _same_ list, and use them to send many requests and to update
the _same_ list with multiple responses. This is utterly error-prone and
can lead to really complex bugs to discover.

Moreover, it's a lot of clones. Cloning a
`SlidingSyncListRequestGenerator` isn't cheap as it means cloning a
`SlidingSyncList`. Moreover, cloning a `SlidingSyncList` isn't cheap as
it means cloning the entire struct.

Having `SlidingSyncListRequestGenerator` inside the module API also
makes the code of `SlidingSync` more complex (why having to deal with
lists and request generators at the same time? we must be very careful
to maintain both side by side? what if a list is removed but not its
request generator? and so on).

So. This patch simplifies all that.

First off, it extracts all the fields of `SlidingSyncList` into a
`SlidingSyncListInner` struct. Then, `SlidingSyncList` has only one
field: `Arc<SlidingSyncListInner>`. Boom, it's now cheap to clone it.

Second, `SlidingSyncListRequestGenerator` is only a
struct with constructors, but there is no extra methods.
`SlidingSyncListRequestGenerator` _no longer_ contains a
`SlidingSyncList`, and doesn't no need a list at all to work. It's just
values.

Third, `SlidingSyncList` holds a `SlidingSyncListRequestGenerator`, and
only one.

Fourth, `SlidingSyncList` (and `SlidingSyncListInner`) now has methods
to handle the internal request generator. The initial `impl Iterator for
SlidingSyncListRequestGenerator` becomes a simple
`SlidingSyncList::next_request` method.

Fifth, previously, the `SlidingSyncList::handle_response`
was never called directly by `SlidingSync`. It was called by
`SlidingSyncListRequestGenerator`! How confusing! `SlidingSync` called
`SlidingSyncListRequestGenerator::handle_response` which was calling
`SlidingSyncList::handle_response`. Now, the flow is more natural:
`SlidingSync` calls `SlidingSyncList::handle_response` and that's it.

Sixth, the `SlidingSyncList::handle_response` is now composed of 2
parts: updating the list itself, and updating the request generator. It
was kind of the case before, but onto two different types.
It was unclear which types were updating `SlidingSyncList`.
For example, `SlidingSyncList::state` was updated by…
`SlidingSyncListRequestGenerator`. Now `SlidingSyncList` is responsible
to update itself, and no one else.

Finally, `SlidingSync` no longer have to deal with
`SlidingSyncListRequestGenerator`. All it has is a set of
`SlidingSyncList`, and that's it!

The tests are still passing, hurray.
2023-03-22 13:17:50 +01:00
Ivan Enderlin d81e6a18f9 chore(sdk): Format a comment. 2023-03-22 13:15:50 +01:00
Ivan Enderlin e514415642 test(sdk): Write test suites for SlidingSyncList and siblings
test(sdk): Write test suites for `SlidingSyncList` and siblings
2023-03-22 12:29:27 +01:00
Ivan Enderlin d9096cc64c chore(sdk): Make Clippy happy. 2023-03-22 12:14:04 +01:00
Ivan Enderlin f085289d05 chore(sdk): Make Clippy happy. 2023-03-22 11:37:51 +01:00
Damir Jelić ef81168434 Fix some doc links in the send_attachment docs 2023-03-22 11:36:02 +01:00
Ivan Enderlin 1f04353668 test(sdk): Use vector! from imbl, not im. 2023-03-22 11:20:07 +01:00
Alfonso Grillo f70b6f5ecf Expose 'search users' to UniFFI (#1689) 2023-03-22 11:15:03 +01:00
Ivan Enderlin ee7dc2a7ab chore(sdk): Address PR feedback. 2023-03-22 11:06:21 +01:00
Ivan Enderlin 1256052134 chore(sdk): Clean up. 2023-03-22 11:06:21 +01:00
Ivan Enderlin 28eebe2043 doc(sdk): Explain what SlidingSyncList::handle_response does and does not. 2023-03-22 11:06:21 +01:00
Ivan Enderlin bb54ff7991 chore(sdk): Move and document the SlidingSyncList::request_generator method. 2023-03-22 11:06:21 +01:00
Ivan Enderlin aa75d02ae3 feat(sdk): SlidingSyncList::handle_response must be pub(self).
This method must be only visible to the current module, not from the
upper/super module.

Note: `pub(self)` is equivalent to no `pub` at all.
2023-03-22 11:06:21 +01:00
Ivan Enderlin eefef9a81b test(sdk): Move test to appropriate files. 2023-03-22 11:06:21 +01:00
Ivan Enderlin 15646ec1d6 test(sdk): Fix how TimelineEvent is constructed. 2023-03-22 11:06:21 +01:00
Ivan Enderlin 10380596cd chore(sdk): Format a comment. 2023-03-22 11:06:21 +01:00
Ivan Enderlin c4df1cb9aa chore(sdk): Rename an argument. 2023-03-22 11:06:21 +01:00
Ivan Enderlin c509b6c76a test(sdk): Add tests for SlidingSyncList.set_range, add_range and reset_ranges. 2023-03-22 11:06:21 +01:00
Ivan Enderlin 9c2e2238ed test(sdk): Add a test for SlidingSyncList::ranges. 2023-03-22 11:06:21 +01:00
Ivan Enderlin 2edb5d845e feat(sdk): SlidingSyncList.ranges & co. takes a Into<UInt>.
In `SlidingSyncListBuilder`, the `ranges`, `set_range`, and `add_range`
methods do not take a `u32` but a `U: Into<UInt>`. That's great!

However, in `SlidingSyncList`, the same methods take a `u32`. It makes
the API inconsistent.

This patch fixes that.
2023-03-22 11:06:18 +01:00
Ivan Enderlin 147e5f8f03 fix(sdk): Rename SlidingSyncList.set_ranges to ranges.
This patch renames `SlidingSyncList.set_ranges` to `ranges` so that
it matches the same terminology of the `SlidingSyncListBuilder` with
`ranges`, `set_range`, `add_range` and `reset_ranges`.

Consistency is important here.
2023-03-22 10:59:23 +01:00
Ivan Enderlin 8e9cabcbf9 test(sdk): Add test for SlidingSyncList::new_builder. 2023-03-22 10:59:23 +01:00
Ivan Enderlin 42af266806 fix(sdk): Fix SlidingSyncList::new_builder.
The `SlidingSyncList::new_builder` method creates a new builder from a
`SlidingSyncList`. This method was missing some configurations, like
`full_sync_maximum_numbre_of_rooms_to_fetch`, `send_updates_for_items`,
`filters`, `ranges` and `timeline_limit`.

This patchs adds those missing configurations.
2023-03-22 10:59:23 +01:00
Ivan Enderlin 7a1fb0b368 fix(sdk): Remove useless fields on SlidingSyncListBuilder.
The `SlidingSyncListBuilder` struct has a `state` and a `rooms_list`
fields. Those fields are never updated and no setters or getters exist
to use them. There are just set with defaults, and passed to the final
`SlidingSyncList` type within the `build` method.

If a field is present in a builder type, it means they can be modified,
but it's not the case. So this patch removes them.
2023-03-22 10:59:17 +01:00
Ivan Enderlin 3f72b10831 test(sdk): Write a test case for FrozenSlidingSyncList when serialized. 2023-03-22 10:58:40 +01:00
Ivan Enderlin 6bd95057b6 test(sdk): Write a test case for RoomListEntry when serialized. 2023-03-22 10:58:40 +01:00
Ivan Enderlin 859317a0f6 test(sdk): Write test suites for SlidingSyncState and SlidingSyncMode. 2023-03-22 10:58:40 +01:00
Ivan Enderlin ace3ff3754 test(sdk): Write test suites for RoomListEntry. 2023-03-22 10:58:40 +01:00
Ivan Enderlin 60b627c2ca feat(sdk): Rename RoomListEntry.freeze to .freeze_by_ref. 2023-03-22 10:58:40 +01:00
Ivan Enderlin 41615840dc feat(sdk): Remove deduplication logic from Sliding Sync.
Event deduplication is supposed to be handled by the `Timeline`. Sliding
Sync is not responsible to de-duplicate the events, in our model. Having
two different de-duplication logics (one in Sliding Sync, and one in the
Timeline) could create weird situations, and hard to debug issues.

We give it a try by removing the deduplication from Sliding Sync.
2023-03-22 10:53:23 +01:00
Ivan Enderlin d07b389f4b chore(sdk): Rename some variables. 2023-03-22 10:52:56 +01:00
Damir Jelić b2a35fc9cf Merge branch 'main' into valere/msc_2399 2023-03-21 16:41:55 +01:00
Damir Jelić 900a4f848c Don't log verification requests as sent if the request id doesn't match 2023-03-21 15:57:16 +01:00
Damir Jelić c4d73c9901 fixup! Don't send m.no_olm withheld codes out if another session is doing so 2023-03-21 15:56:58 +01:00
Damir Jelić eb9dd8fb0d Don't send m.no_olm withheld codes out if another session is doing so
The `m.no_olm` withheld code is supposed to be sent out only once for a
given device.

This patch prevents double sending of m.no_olm withheld code by adding
a test that ensures only one group session will send it out.

Previously, the logic for sharing group sessions was mostly correct, but
an edge case was forgotten where two group sessions attempted to share
the withheld code concurrently.

Although there's still a possibility for multiple m.no_olm withheld codes
to be sent out. This may happen during the propagation of the flag
remembering the fact that the `m.no_olm` code has been sent from the
OutboundGroupSession to Device. This scenario is likely unrealistic and
the consequences of sending things twice aren't problematic.
2023-03-21 15:45:32 +01:00
Jonas Platte a134cc378d Sort sliding-sync-integration-test dependencies 2023-03-21 13:31:58 +01:00
Jonas Platte a32fce6cdf Make all sliding-sync-integration-test dependencies dev-dependencies 2023-03-21 13:31:58 +01:00
Jonas Platte 9ee2d04a41 Feature-gate everything in sliding-sync-integration-test 2023-03-21 12:24:14 +01:00
Mauro 1ce1c5636e Add support for (un)ignoring users 2023-03-21 12:23:17 +01:00
Damir Jelić 8652cdf752 Test the VerificationState migration 2023-03-21 12:02:31 +01:00
Damir Jelić 32e2ea0288 Allow the old VerificationState enum to be deserialized into the new one 2023-03-21 12:02:31 +01:00
Damir Jelić 7263914f67 Remove the Apple specific auth service tests
These tests are doing real network requests towards hosts that are not
under our control.
2023-03-21 12:00:43 +01:00
Florian Renaud 76763a80fe ffi: Add binding for get_dm_room 2023-03-20 16:30:25 +00:00
Jonas Platte 72ae9dd885 Upgrade eyeball-im 2023-03-20 16:51:28 +01:00
Jonas Platte 816e722807 ffi: Inline uniffi_api modules
include_scaffolding! is expected to be used at the crate root now, and
clippy seems happy with the generated code right now.
2023-03-20 15:42:38 +01:00
Jonas Platte 1a1fe97d00 Use Rust conventions for variable names in UDL 2023-03-20 15:42:38 +01:00
Jonas Platte 16687f24f9 sdk: Fix documentation of create_room 2023-03-20 13:56:43 +01:00
Jonas Platte ea41076c82 sdk: Rename create_dm_room to create_dm and make it public 2023-03-20 13:56:43 +01:00
Jonas Platte 130dc58a5d Remove redundant cfg attributes
The whole encryption module is only enabled with e2e-encryption.
2023-03-20 13:56:43 +01:00
Jonas Platte 9d6e192b9f sdk: Move create_dm_room out of encryption module
Creating DM rooms makes sense for non-encryption-capable client as well.
2023-03-20 13:56:43 +01:00
Jonas Platte 82d1d64f85 sdk: Simplify create_dm_room
It doesn't need to call mark_as_dm itself as create_room will do that.
2023-03-20 13:56:43 +01:00
Jonas Platte 39a4dc911f Remove event contents from (Sync)TimelineEvent Debug impls 2023-03-20 13:21:41 +01:00
Alfonso Grillo a7ed8e0b45 Update account_data when creating a new dm room 2023-03-20 12:08:21 +01:00
Ivan Enderlin e64ab6cf9c Merge pull request #1677 from matrix-org/rav/update-js-bindings-prefix
matrix-sdk-crypto-js: drop `v` from release tags
2023-03-20 09:23:29 +01:00
Damir Jelić 43d883da9c Refactor the room key sharing logic 2023-03-16 19:40:30 +01:00
Richard van der Hoff 42dce635de matrix-sdk-crypto-js: drop v from release tags
Currently, the tags used for releases of this binding have the format:
"matrix-sdk-crypto-js-v0.1.0-alpha.5". This is inconsistent with tags used for
other parts of the project, which omit the `v` prefix.
2023-03-16 17:49:52 +00:00
Richard van der Hoff 7794b230e1 Merge pull request #1675 from matrix-org/release-matrix-sdk-crypto-js-v0.1.0-alpha.5
matrix-sdk-crypto-js: prepare v0.1.0-alpha.5
2023-03-16 17:38:33 +00:00
Ivan Enderlin 2d56f550aa fix(sdk): Fix, test, and clean up SlidingSyncListRequestGenerator
fix(sdk): Fix, test, and clean up `SlidingSyncListRequestGenerator`
2023-03-16 17:04:05 +01:00
Ivan Enderlin b39a224be8 doc(sdk): Use the Rust range notation to avoid ambiguity. 2023-03-16 17:03:25 +01:00
Richard van der Hoff e6bf74b7db matrix-sdk-crypto-js v0.1.0-alpha.5
Release `crypto-js` / Publish 🕸 [m]-crypto-js (push) Failing after 41s
2023-03-16 13:47:55 +00:00
Ivan Enderlin 5f06dc8229 doc(sdk): Fix a typo. 2023-03-16 11:53:01 +01:00
Kévin Commaille 46d8d26b71 sdk: Fix a typo
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-16 11:40:47 +01:00
Kévin Commaille e23be44345 sdk: Store OIDC issuer as a String rather than a Url
The url crate normalizes the string, but during OIDC verification steps,
the issuer verification must be made against the exact string that was
provided.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-16 11:40:47 +01:00
Ivan Enderlin 57fb659b86 Merge pull request #1666 from zecakeh/indexeddb-crypto-version
indexeddb: Use u32 to represent version of crypto store
2023-03-16 11:12:27 +01:00
Ivan Enderlin 1933fe7a8f doc(sdk): Fix a typo. 2023-03-16 11:04:18 +01:00
Ivan Enderlin 6a6d94a065 Re-expose the vodozemac and matrix-sdk-crypto versions in the bindings
Re-expose the vodozemac and matrix-sdk-crypto versions in the bindings
2023-03-16 10:56:51 +01:00
Ivan Enderlin ccfb66c576 chore(crypto-nodejs): Fix a lint. 2023-03-16 10:39:43 +01:00
Ivan Enderlin c2d3afffff feat(crypto-nodejs): Make Versions a class, not a JS object. 2023-03-16 10:33:41 +01:00
Ivan Enderlin e1f6fd8a1e feat(ffi): Create the version and vodozemac_version functions. 2023-03-16 09:53:39 +01:00
Damir Jelić 2f377d536a fixup! Re-expose the vodozemac and matrix-sdk-crypto versions in the bindings 2023-03-16 09:53:39 +01:00
Damir Jelić 26789f22b0 fixup! Re-expose the vodozemac and matrix-sdk-crypto versions in the bindings 2023-03-16 09:53:39 +01:00
Damir Jelić 3aa1c30f5c Re-expose the vodozemac and matrix-sdk-crypto versions in the bindings 2023-03-16 09:53:37 +01:00
Ivan Enderlin 5e23bd09bc feat(ci): aarch64-apple-ios should work on rustc stable. 2023-03-16 09:01:14 +01:00
Ivan Enderlin c8bad4b86e doc(sdk): Fix a typo. 2023-03-16 08:55:25 +01:00
Ivan Enderlin 07c366983d doc(sdk): Fix a typo. 2023-03-16 08:24:05 +01:00
Ivan Enderlin 51abedda59 feat(ffi): Update SlidingStateState variants. 2023-03-16 08:21:11 +01:00
Ivan Enderlin 9d5a1fda3c doc(sdk): Fix typos or improve. 2023-03-16 08:11:45 +01:00
Damir Jelić 61ea15eb39 Merge branch 'main' into valere/msc_2399 2023-03-15 19:31:04 +01:00
Kévin Commaille fe69948926 sdk: Add a method to change the room name
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-15 17:28:08 +00:00
Valere 248b8db309 Extended verification states
Co-authored-by: Damir Jelić <poljar@termina.org.uk>
Co-authored-by: Denis Kasak <dkasak@termina.org.uk>
2023-03-15 18:16:31 +01:00
Ivan Enderlin ca5cabb7e1 doc(sdk): Fix a typo. 2023-03-15 18:08:19 +01:00
Ivan Enderlin 7a2b1e6e1f doc(sdk): Fix a typo. 2023-03-15 17:59:11 +01:00
Ivan Enderlin c0f84bb8da test(sdk): Fix tests and improve speed.
To improve the speed, we simply reduce the numbers of rooms involved in
the test.
2023-03-15 17:54:39 +01:00
Kévin Commaille 9a9b142630 indexeddb: Use u32 to represent version of crypto store
Like for the state store, the bindings expose the version as an f64
while the web API expects an unsigned integer.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-15 17:08:33 +01:00
Ivan Enderlin a2dcfa905f fix(sdk): Fix, test, and clean up SlidingSyncList.
This patch should ideally be split into multiple smaller ones, but life
is life.

This main purpose of this patch is to fix and to test
`SlidingSyncListRequestGenerator`. This quest has led me to rename
mutiple fields in `SlidingSyncList` and `SlidingSyncListBuilder`, like:

* `rooms_count` becomes `maximum_number_of_rooms`, it's not something
  the _client_ counts, but it's a maximum number given by the server,

* `batch_size` becomes `full_sync_batch_size`, so that now, it
  emphasizes that it's about full-sync only,

* `limit` becomes `full_sync_maximum_number_of_rooms_to_fetch`, so that
  now, it also emphasizes that it's about ful-sync only _and_ what the
  limit is about!

This quest has continued with the renaming of the `SlidingSyncMode`
variants. After a discussion with the ElementX team, we've agreed on the
following renamings:

* `Cold` becomes `NotLoaded`,
* `Preload` becomes `Preloaded`,
* `CatchingUp` becomes `PartiallyLoaded`,
* `Live` becomes `FullyLoaded`.

Finally, _le plat de résistance_.

In `SlidingSyncListRequestGenerator`, the `make_request_for_ranges`
has been renamed to `build_request` and no longer takes a `&mut self`
but a simpler `&self`! It didn't make sense to me that something
that make/build a request was modifying `Self`. Because the type of
`SlidingSyncListRequestGenerator::ranges` has changed, all ranges now
have a consistent type (within this module at least). Consequently, this
method no longer need to do a type conversion.

Still on the same type, the `update_state` method is much more
documented, and errors on range bounds (offset by 1) are now all fixed.

The creation of new ranges happens in a new dedicated pure function,
`create_range`. It returns an `Option` because it's possible to not be
able to compute a range (previously, invalid ranges were considered
valid). It's used in the `Iterator` implementation. This `Iterator`
implementation contains a liiiittle bit more code, but at least now
we understand what it does, and it's clear what `range_start` and
`desired_size` we calculate. By the way, the `prefetch_request` method
has been removed: it's not a prefetch, it's a regular request; it was
calculating the range. But now there is `create_range`, and since it's
pure, we can unit test it!

_Pour le dessert_, this patch adds multiple tests. It is now
possible because of the previous refactoring. First off, we test the
`create_range` in many configurations. It's pretty clear to understand,
and since it's core to `SlidingSyncListRequestGenerator`, I'm pretty
happy with how it ends. Second, we test paging-, growing- and selective-
mode with a new macro: `assert_request_and_response`, which allows to
“send” requests, and to “receive” responses. The design of `SlidingSync`
allows to mimic requests and responses, that's great. We don't really
care about the responses here, but we care about the requests' `ranges`,
and the `SlidingSyncList.state` after a response is received. It also
helps to see how ranges behaves when the state is `PartiallyLoaded`
or `FullyLoaded`.
2023-03-15 16:57:29 +01:00
Kévin Commaille 1e1e0d7e6d ci: Make sure all rendered docs examples are compiling
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-15 14:42:12 +01:00
Kévin Commaille 5d1a738162 sdk: Fix generate_image_thumbnail example in docs
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-15 14:42:12 +01:00
Jonas Platte e4e9efb7d9 ffi: Put CreateRoomParameters back into the UDL file
… to restore the default values of some fields.
2023-03-15 13:09:54 +01:00
Doug b1074e400e sdk: Add get_media_file function 2023-03-15 09:22:53 +00:00
Ivan Enderlin 9574f93320 feat(sdk): Add more Sliding Sync logs
feat(sdk): Add more Sliding Sync logs
2023-03-15 09:58:02 +01:00
Ivan Enderlin c3bcbf8952 chore(sdk): Rephrase logs a little bit. 2023-03-15 09:41:39 +01:00
Jonas Platte 739dd8cf93 Fix clippy complaint 2023-03-14 18:34:23 +01:00
Jonas Platte c7e1d617ec ffi: Use UniFFI derives for types no longer referenced in UDL 2023-03-14 18:34:23 +01:00
Jonas Platte ae85e18588 ffi: Use UniFFI proc-macros for more Client methods 2023-03-14 18:34:23 +01:00
Damir Jelić 516d849ef2 Allow room key forwarding to be enabled and disabled (#1365)
Co-authored-by: Ivan Enderlin <ivan@mnt.io>
2023-03-14 18:23:39 +01:00
Damir Jelić eb2bf61236 Merge pull request #1657 from matrix-org/poljar/session-creator-info
Improve the docs of the inbound group session type
2023-03-14 17:33:24 +01:00
Damir Jelić 0985043a6a Apply suggestions from code review
Co-authored-by: Denis Kasak <dkasak@termina.org.uk>
2023-03-14 17:01:03 +01:00
Damir Jelić 36b739b830 Improve the docs of the inbound group session type 2023-03-14 15:50:03 +01:00
Doug eeea4f23bc fix(bindings): More authentication service server name fixes
- Trim any trailing slashes
- If server name parsing fails, try as a URL instead of throwing
- Add tests
- Fix typo & clippy
2023-03-14 14:40:19 +00:00
Alfonso Grillo c9a6898c73 Add a guide for contributing to UniFFI bindings 2023-03-14 15:39:09 +01:00
Damir Jelić 18c33aefa1 Merge pull request #1633 from matrix-org/rav/fix_wait_if_user_pending
Fix races in `wait_if_user_pending`
2023-03-14 14:59:20 +01:00
Damir Jelić 2c7c251e47 Silence an invalid clippy warning 2023-03-14 14:45:19 +01:00
Damir Jelić f3ee6c735b Fix some formatting issue where cargo fmt failed to take care of things 2023-03-14 14:28:34 +01:00
Damir Jelić 4f9cb48ef8 Use a Display implementation to record the SequenceNumber in logs 2023-03-14 14:28:27 +01:00
Damir Jelić 9880619d81 Merge branch 'main' into rav/fix_wait_if_user_pending 2023-03-14 14:26:28 +01:00
Mauro a8204987be Add convenience methods for updating a room's avatar 2023-03-14 13:03:52 +00:00
Kévin Commaille cc585974e0 sdk: Get push actions of events received via back pagination
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-14 14:02:01 +01:00
Kévin Commaille aebd5fe4eb sdk: Fix timeline highlight test
Update Ruma to fix the server default push rules.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-14 14:02:01 +01:00
Mauro 7ab134233e bindings: Set pusher from client + notification service stub
Co-authored-by: ismailgulek <ismailg@matrix.org>
2023-03-14 13:36:36 +01:00
Kévin Commaille 0f54877b31 feat(sdk): Add methods to only send receipts newer than the current ones in the timeline
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-14 13:32:30 +01:00
Kévin Commaille 1c9aa7c907 refactor(sdk): Add private method to get the fully-read event for a timeline.
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-14 13:32:30 +01:00
Damir Jelić 4041c875d1 Merge pull request #1460 from matrix-org/andy/enhance_store
Store encryption settings
2023-03-14 12:36:42 +01:00
Andy Uhnak d226738d15 More pub(crate) methods 2023-03-14 10:43:40 +00:00
Andy Uhnak 404b95ab84 Missing migration method in UDL 2023-03-14 10:28:41 +00:00
Andy Uhnak 0d082f6b96 Update js tests 2023-03-14 10:28:15 +00:00
Andy Uhnak 5bf9182549 Fix issues after rebase 2023-03-14 10:28:15 +00:00
Andy Uhnak 1177bdd394 Indexdb migration 2023-03-14 10:28:15 +00:00
Andy Uhnak 98ca774118 Constrain store methods 2023-03-14 10:28:12 +00:00
Andy Uhnak 8f13023333 Missing stores 2023-03-14 10:27:11 +00:00
Andy Uhnak 51b3bf4c75 Add room settings migration 2023-03-14 10:27:11 +00:00
Andy Uhnak 283137f190 Introduce RoomSettings 2023-03-14 10:27:11 +00:00
Andy Uhnak 33b348c376 Constrain new api to Store struct 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
Jonas Platte 0f7d839c49 Undo room_type => room_state rename in serialized form of RoomInfo 2023-03-13 16:45:44 +01:00
Jonas Platte ee6c0d9486 Simplify TimelineEvent construction 2023-03-13 16:31:19 +01:00
Jonas Platte 7121179b6a sdk: Make test code less repetitive 2023-03-13 16:31:19 +01:00
Jonas Platte 59edc22a35 Use Action covenience methods
Now Action::Coalesce will also generate a notification.
2023-03-13 16:31:19 +01:00
Jonas Platte e9058f58be Upgrade Ruma 2023-03-13 16:31:19 +01:00
Jonas Platte 7dee9648cf sdk: Clone VerificationRequest twice in generate_qr_code method again
… required for the returned Future to be Send.
2023-03-13 15:34:14 +01:00
Alfonso Grillo 786e9b5db9 Add createRoom UniFFI binding 2023-03-13 15:33:18 +01:00
valere e20db574ec Merge branch 'main' into valere/msc_2399 2023-03-13 14:51:23 +01:00
Ivan Enderlin c6c259144a chore(sdk): Rename enum variants to be consistent across all the modules. 2023-03-13 14:29:12 +01:00
Damir Jelić 58e99a645f Don't use a type alias for the sequence number
Using a wrapper type allows us to hide the internals a bit better and
we're now implementing Ord which is used in the comparison.
2023-03-13 14:17:12 +01:00
Damir Jelić 5d6cf2d33a Remove some clones when creating a /keys/query request
The part of code that converts a list of users to the actual
/keys/query request uses the chunks() method. This method operates on
the slice. Our list/vec of users gets dereferenced into a slice before
we create our chunks. The chunks can't take ownership of the data, which
in turn requires us to clone the user IDs for them to be put into the
request.

Itertools has a chunks() method which operates on an iterator which we
can utilize to remove, not only the clone, but also a collect call.

At the same time, let's make the conversion step a simple functional
mapping and document what's going on.
2023-03-13 14:17:12 +01:00
Damir Jelić 5f1a22e26f Remove some useless ? -> Ok invocations
We can just return the result directly instead of doing the error
conversion dance using the question mark operator
2023-03-13 14:17:12 +01:00
Damir Jelić 6afb6f1e27 Use an Option for the KeysQueryDetails instead of a default value
The Option conveys our intention a bit better compared to the default
value. While nothing bad can happen, since the request IDs map will be
empty by default, it's a bit scary to have a valid sequence number since
an i64 defaults to 0.
2023-03-13 14:17:12 +01:00
Ivan Enderlin 032356566e chore(sdk): Rewrite a small part of the code to make it clearer. 2023-03-13 14:07:36 +01:00
Ivan Enderlin d7468ec158 Update crates/matrix-sdk/src/sliding_sync/mod.rs
Co-authored-by: Jonas Platte <jplatte+git@posteo.de>
2023-03-13 13:49:21 +01:00
Kévin Commaille 929538608a timeline: Expose if an event should be highlighted
Only events received via sync.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-13 13:47:06 +01:00
Kévin Commaille d5a56bcbc7 sdk: Expose calculated push actions for event handlers
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-13 13:47:06 +01:00
Kévin Commaille 84ff8980f3 sdk-base: Expose calculated push actions on event
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-13 13:47:06 +01:00
Jonas Platte 84d83dda0a base: Add RoomInfo::state accessor 2023-03-13 13:17:15 +01:00
Jonas Platte fb4b347433 base: Rename RoomType to RoomState 2023-03-13 13:17:15 +01:00
Richard van der Hoff e1c6ac9787 Merge branch 'main' into rav/fix_wait_if_user_pending 2023-03-13 11:52:11 +00:00
Ivan Enderlin 35bb18a1e8 feat(sdk): Add more Sliding Sync logs.
This patch adds more logs around `SlidingSync` to ease debugging sessions.
2023-03-13 11:41:20 +01:00
Ivan Enderlin 483bef0748 Merge pull request #1645 from Hywan/fix-sdk-fixme 2023-03-13 11:21:17 +01:00
Ivan Enderlin 3c25ad1489 doc(sdk): Remove a FIXME that is actually fixed.
The #1386 issue is closed. Yepee.
2023-03-13 09:05:32 +01:00
Jonas Platte 8d83a996a6 Handle another new clippy lint
Issue about false positives:
https://github.com/rust-lang/rust-clippy/issues/10482
2023-03-11 13:13:50 +01:00
Jonas Platte 595e34aad5 Silence new clippy lint for generated code 2023-03-11 13:13:50 +01:00
Jonas Platte cc35ee72b8 Upgrade eyeball to 0.4.0 2023-03-11 13:13:50 +01:00
Jonas Platte b148ea2fb1 Bump locked version of clap
To work around this clippy bug:
https://github.com/rust-lang/rust-clippy/issues/10421
2023-03-11 13:13:50 +01:00
Mauro 2bb66f0f2e bindings: Expose leave and reject_invitation functions to FFI 2023-03-10 18:07:13 +01:00
valere 8057a3691f unused import 2023-03-10 16:00:22 +01:00
valere 057cf0bde7 unneeded trait impl 2023-03-10 08:57:56 +01:00
valere c43d9d3e43 post rebase fix 2023-03-09 17:27:34 +01:00
Ivan Enderlin 331ea35be6 fix(sdk): SlidingSync has a lock for both pos _and_ delta_token
fix(sdk): `SlidingSync` has a lock for both `pos` _and_ `delta_token`
2023-03-09 17:10:42 +01:00
Richard van der Hoff fc9dbae23b fixup! Rewrite wait_if_user_pending to fix races 2023-03-09 16:06:37 +00:00
valere 9733d9842a Merge branch 'main' into valere/msc_2399 2023-03-09 17:04:21 +01:00
Ivan Enderlin b9fd451d54 feat(sdk): Move Client.root_span inside ClientInner
feat(sdk): Move `Client.root_span` inside `ClientInner`
2023-03-09 17:03:27 +01:00
Ivan Enderlin 3511a4a053 fix(sdk): SlidingSync has a lock for both pos _and_ delta_token.
This patch updates `SlidingSync.pos` and `.delta_token`. Each field has their
own lock. It's annoying because they must updated at the same time to not be
out-of-sync.

So a new field `SlidingSync.position` of type `SlidingSyncPositionMarkers` is
created. It holds `pos` and `delta_token. This new field is behind a single
`RwLock`.
2023-03-09 16:47:11 +01:00
Ivan Enderlin 7f52292e92 chore(sdk): Remove useless comment. 2023-03-09 16:43:41 +01:00
valere 980c9cb4f5 report partially withheld error as MissingRoomKey 2023-03-09 16:39:02 +01:00
valere 2368ac74de feat(withheld) expose withheld code in error binding 2023-03-09 16:38:38 +01:00
Ivan Enderlin cc69efc3f8 feat(sdk): Move Client.root_span inside ClientInner. 2023-03-09 15:12:11 +01:00
Richard van der Hoff 5a06c11ac0 Move UserKeyQueryResult to store
Now that `wait_if_user_key_query_pending` is in Store, it makes more sense that
the result type be in the same module.
2023-03-09 13:11:34 +00:00
Richard van der Hoff d2fdc20733 Rip out redundant KeysQueryListener 2023-03-09 13:11:34 +00:00
Richard van der Hoff 16871d1dcd Rewrite wait_if_user_pending to fix races 2023-03-09 13:11:34 +00:00
Richard van der Hoff 508f1bc976 Fix races between /sync and /keys/queries (#1619)
The comments in the code should explain the general approach, but to give an overview:

We assign a unique sequence number to each device-list-invalidation notice, and keep track 
of the sequence number at which each user was most recently invalidated. Then, when we 
build a `/keys/query` request, we take note of the current sequence number. Finally, when 
we get the response from that `/keys/query` request, we compare that sequence number
with the most-recent-invalidation of each user that is now supposedly up-to-date. All 
of that means that we can see if the user has been invalidated *since* we built the 
`/keys/query` request, in which case our request may have raced against the new device,
so we do not mark the user as up-to-date.

To make that work, we need to keep track of the sequence number for in-flight `/keys/query`
requests; we do that in the `IdentityManager`. Since there should only ever be at most one
batch of requests in flight, that is relatively easy; however, we also record the request ids
just to check that the application isn't doing something weird.

There is no need to persist the sequence numbers to permanent storage: provided the
`IdentityManager` and `Store` objects have a lifetime at least as long as an in-flight
`/keys/query` request, it is sufficient just to retain the `dirty` bit in permanent storage,
and then, when we reload the ephemeral `Store` cache, to treat everything with a `dirty`
bit as a "new" invalidation.

Fixes: #1386.
2023-03-09 10:57:20 +00:00
Ivan Enderlin 0419967158 doc(sdk): Rephrase the Quick refreshing Section.
With https://github.com/matrix-org/matrix-rust-sdk/pull/1601, it's safer to
cancel a stream at any time, as any `Future` cancellation. This documentation
section of `SlidingSync` is no longer relevant as is, and can be rephrased.
2023-03-09 11:56:33 +01:00
Ivan Enderlin da5c79482b feat(ffi): Rethink TaskHandle
feat(ffi): Rethink `TaskHandle`
2023-03-08 17:21:52 +01:00
Ivan Enderlin 4996a19b31 feat(ffi): Rethink TaskHandle.
With https://github.com/matrix-org/matrix-rust-sdk/pull/1601,
`TaskHandle::with_callback` is no longer necessary.

This patch updates `TaskHandle` as follows:

1. Before, the `handle` and `callback` fields were not mutually exclusive!
   Indeed, the `callback` field was used as an abort mechanism, but _also_ as a
   finalizer, i.e. a code that runs _after_ the cancellation of `handle`. Now
   that the callback-based solution is no longer used, we can keep one usage for
   this field and rename it `finalizer`.
2. The `handle` field is no longer optional, as it will always be set.
3. The `with_callback` method is renamed `new` as it's now the only constructor.
4. The `set_callback` method is renamed `set_finalizer`.

Tadaaa.
2023-03-08 17:09:47 +01:00
Ivan Enderlin d720861fbd fix(bindings): SlidingSync.sync returns an immediately cancellable TaskHandle
fix(bindings): `SlidingSync.sync` returns an immediately cancellable `TaskHandle`
2023-03-08 16:39:09 +01:00
Ivan Enderlin c517f38ec8 sliding-sync: process receipt extension response
sliding-sync: process receipt extension response
2023-03-08 16:29:56 +01:00
Ivan Enderlin ac863409bb doc(sdk): Remove notion of fairness. 2023-03-08 16:20:50 +01:00
Kegan Dougal 417008cc87 Clippy 2023-03-08 15:14:59 +00:00
Ivan Enderlin 5dd404d2f1 doc(sdk): Precise in which order SS responses will be handled. 2023-03-08 16:09:05 +01:00
Kegan Dougal 2c0466a8cc Unbreak tests; don't shadow 2023-03-08 15:03:41 +00:00
kegsay aec65c818b Update testing/sliding-sync-integration-test/src/lib.rs
Co-authored-by: Ivan Enderlin <ivan@mnt.io>
2023-03-08 14:51:26 +00:00
Jonas Platte 0d9d130833 Replace StateStoreDataKey::encoding_key by associated constants 2023-03-08 15:51:13 +01:00
Jonas Platte 26e259455a Adhere to module naming conventions 2023-03-08 15:51:13 +01:00
Ivan Enderlin 2f637cda6f doc(sdk): Add note about fairness of the SS lock. 2023-03-08 15:33:40 +01:00
Ivan Enderlin 64ae77ec70 feat(sdk): Run SS response handling in a single non-cancellable block. 2023-03-08 15:15:16 +01:00
Jonas Platte 932cf2ad99 Fix experimental features not compiling without encryption 2023-03-08 15:03:20 +01:00
Jonas Platte 68f8ed5a92 Add futures-util as a workspace dependency
… and always activate its `alloc` feature.
Fixes building matrix-sdk without the e2e-encryption feature.
2023-03-08 15:03:20 +01:00
Kegan Dougal 4670142a83 Clippy 2023-03-08 13:21:56 +00:00
Kegan Dougal 9a53602d73 Formatting 2023-03-08 12:58:48 +00:00
Kegan Dougal 48bf20fcca Add integration test for receipts in sliding sync 2023-03-08 12:53:23 +00:00
Mauro be6c7c8b4c Add user avatar URL caching 2023-03-08 13:30:46 +01:00
Ivan Enderlin 832146b43d feat(sdk): Create SlidingSyncInner.
Move all `SlidingSync`'s fields into a new `SlidingSyncInner` type, and then
update `SlidingSync` to contain a single `inner: Arc<SlidingSyncInner>` field.

First off, it's much simpler to understand what's behind an `Arc` for
“clonability” of `SlidingSync`, and what's not. We don't need to worry about
adding a new field that is not behind an `Arc` for a specific reason or
anything. The pattern is clear now.

Second, this is required for next commits.
2023-03-08 12:12:58 +01:00
Ivan Enderlin 66d4ced90f chore: Add some inline documentation. 2023-03-08 12:01:57 +01:00
Jonas Platte 5c2915bb6a crypto-ffi: Use uniffi derives for types no longer referenced in UDL 2023-03-08 11:12:11 +01:00
Jonas Platte fb23cbff97 crypto-ffi: Use UniFFI proc-macros for more OlmMachine methods 2023-03-08 11:12:11 +01:00
Jonas Platte 9fe880d05d Clean up formatting of olm.udl 2023-03-08 11:12:11 +01:00
Jonas Platte 7af2bea2fc Upgrade UniFFI 2023-03-08 11:12:11 +01:00
Kegan Dougal f1829faa99 Merge branch 'main' into kegan/receipts-ss 2023-03-08 09:33:48 +00:00
valere a767187ff6 add more tests 2023-03-08 10:06:46 +01:00
Ivan Enderlin 7369010964 feat(sdk): Make SlidingSync::handle_response _sync_, no more _async_.
The idea is to group all async and await points in `sync_once`. It's easier to
think about it.
2023-03-08 09:41:32 +01:00
Ivan Enderlin 07f6a3b345 chore: Remove warnings. 2023-03-08 09:35:47 +01:00
Ivan Enderlin 8f6f20bbe7 fix(bindings): SlidingSync.sync returns an immediately cancellable TaskHandle.
Before this patch, `SlidingSync::sync` was returning a callback-based
`TaskHandle`. It was waiting on the “stream loop” to finish (since it's a long-
poll, it means waiting 2*30s, cf. our code), before checking an atomic flag has
some value to decide whether it was time to leave the loop or not. So when the
user is cancelling this `TaskHandle`, a response (if any) was always handled.
But in the meantime, it was possible to start a new `sync`, and it seems like
it creates bugs.

After this patch, `SlidingSync::sync` now returns a handle-based `TaskHandle`.
It means that cancelling it will cancel the “stream loop” immediately. If
no response was in-flight from the server, that's perfect, no problem. If a
response was in-flight, the inner `pos` of the `SlidingSync` instance won't be
updated as the response won't be handled. So the server will re-send the same
response with the next sync request.

I guess it's better this way. Thoughts?
2023-03-08 09:35:47 +01:00
Ivan Enderlin 6a9cf1f9ce feat(sdk): SlidingSync::stream can match expected responses
feat(sdk): `SlidingSync::stream` can match expected responses
2023-03-08 09:14:32 +01:00
Ivan Enderlin f8ce49cc49 chore: Add documentation. 2023-03-08 08:56:38 +01:00
Ivan Enderlin 46d71acb44 chore: Update ruma to a specific revision. 2023-03-08 08:56:38 +01:00
Ivan Enderlin 9f0563eb95 feat(sdk): SlidingSync::stream can match expected responses.
This patch adds a new feature.

Each call to `SlidingSync::stream` generates a unique `stream_id`. This
`stream_id` is passed to requests as a `txn_id` field. Returned responses must
hold the same `txn_id`, otherwise it means `stream` has received unexpected
responses from another `stream` or something else.

So far, when the `txn_id` field of the response and the `stream_id` don't
match, a log with the `ERROR` level is raised. Should we do more, like ignoring
the response entirely? Not sure yet.
2023-03-08 08:56:32 +01:00
valere bd48cae28d unused import 2023-03-07 19:24:50 +01:00
valere 69749ab8fe feat(withheld) fix experimental-algorithms 2023-03-07 19:07:27 +01:00
valere 97b5a3a61b feat(withheld) cleaning 2023-03-07 19:04:44 +01:00
valere 0ec3aca727 refactor(withheld) store withheld as ShareInfo
and store no_olm sent in device
2023-03-07 18:46:52 +01:00
Kévin Commaille 18e6f10367 examples: Also handle SSO login in the login example
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-07 15:12:33 +01:00
Kévin Commaille d77efd7327 state-store: Replace methods to get and set key-value data
Avoid to make the API bigger when we want to add new key-value data
in the store.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-07 13:52:37 +01:00
Kévin Commaille 5b28c69cfc sled: Rename session store to kv
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-07 13:52:37 +01:00
Kévin Commaille 61796a2725 sled: Move state store keys into keys module
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-07 13:52:37 +01:00
Kévin Commaille f538b5e595 sled: Move the state store migrations to a separate file
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-07 13:52:37 +01:00
Kévin Commaille 4d1363e683 indexeddb: Merge session and sync_token stores into new kv store
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-07 13:52:37 +01:00
Damir Jelić 9324702b04 feat(bindings): Add a method to partially migrate data 2023-03-07 13:29:00 +01:00
Jonas Platte 0c4c73084d Appease clippy 2023-03-07 12:53:32 +01:00
Jonas Platte 6ce51b5890 Remove unnecessary allow attribute 2023-03-07 12:53:32 +01:00
Jonas Platte 3ff1844da4 base: Remove unused Deserialize implementation 2023-03-07 12:53:32 +01:00
Jonas Platte 99cbf3122b ci: Work around frequent codecov upload errors 2023-03-07 11:08:09 +01:00
Kévin Commaille e09ec389d1 indexeddb: Fix migration of state store
The IndexedDB API expects an unsigned integer for the version.
The web-sys bindings expose it as a f64 so versions like 1.1 and 1.2
were used. The were converted back to uint in JS which means the version
was always 1.0 and no real upgrade was processed

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-07 10:04:09 +01:00
Kévin Commaille 2aec3d0502 indexeddb: Move the state store migrations to a separate file
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-07 10:04:09 +01:00
Damir Jelić 2f974c11f7 Simplify handling of sliding sync extensions
Simplify handling of sliding sync extensions

This patch simplifies the logic for handling sliding sync extensions.
These extensions are sticky, meaning that once enabled in one request,
they do not need to be repeatedly enabled for subsequent requests.

However, the previous logic tried to accommodate the case where the
extensions were initially disabled to reduce the size and processing
time of the initial response. This resulted in complex and error-prone
code.

With this patch, we have removed support for disabling to-device events
and streamlined the handling of sync extensions.

Co-authored-by: Jonas Platte <jplatte@matrix.org>
2023-03-07 08:48:05 +01:00
Richard van der Hoff 79ee4b59ee matrix-sdk-crypto: enable tracing for in-crate tests 2023-03-06 19:25:47 +01:00
Jonas Platte 01a66d4874 Fix unnecessary cfg(all()) 2023-03-06 18:25:16 +01:00
valere 1ddcc97f84 refactor(withheld) collect_session_recipient result struct 2023-03-06 12:23:10 +01:00
valere c6a12304ba refactor(msc2399) avoid iterating twice on devices 2023-03-06 11:46:37 +01:00
Jonas Platte 50114f4e74 Mention changelogs in PR template 2023-03-06 10:41:18 +01:00
Jonas Platte 792b60c501 Remove most mentions of conventional commits 2023-03-06 10:41:18 +01:00
Jonas Platte d8465a2c1e Add a very basic changelog to crates/matrix-sdk 2023-03-06 10:41:18 +01:00
Damir Jelić 8550eaeed0 Make sure that syncing members is only happening if they aren't already synced 2023-03-06 10:00:00 +01:00
Damir Jelić 071ba2376e Make the majority of fields of the RoomInfo private 2023-03-06 10:00:00 +01:00
Damir Jelić 7ec98ff721 Remove a race condition when fetching members and sending out room keys 2023-03-06 10:00:00 +01:00
Florian Renaud f03f7c8d1a Fix a typo in the integration tests readme 2023-03-06 09:29:23 +01:00
Stefan Ceriu 0dd2b12141 chore(ffi): Remove now unnecessarly stored is_soft_logout session and client state property. The did_receive_auth_error(is_soft_logout) client delegate is enough. 2023-03-03 18:10:57 +01:00
Stefan Ceriu d513cb9a47 chore(ffi): remove now unused start_sync method and related properties 2023-03-03 18:10:57 +01:00
Stefan Ceriu e550c16c14 Broadcast UnknownToken API client errors, handle them on the FFI client side and send a did_receive_auth_error delegate call 2023-03-03 12:13:26 +01:00
Sam Wedgwood 0da29057f8 feat(sdk): Expose prepare_encrypted_file function
Signed-off-by: Sam Wedgwood <sam@wedgwood.dev>
2023-03-03 09:27:10 +00:00
Kegan Dougal 5881503a6a Clippy 2023-03-02 14:49:16 +00:00
kegsay e261d37756 Apply suggestions from code review
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2023-03-02 14:48:00 +00:00
Richard van der Hoff a2da63758c Use a separate GHA cache for each matrix build (#1605)
You can't update a GHA cache once you create it, so if we use the same cache
for each job in a matrix build, then we end up populating it for the first job
that completes, so any slower jobs don't get their dependencies cached.

On the other hand, if we create 20 500MB cache items on each build, we're going
to exhaust the cache storage as soon as we do a build. So, instead, let's just
do the caching for the main branch, and hope that other branches can still
benefit from it.
2023-03-02 14:10:53 +00:00
Richard van der Hoff a86754a317 Minor tweaks to the github actions configurations (#1606)
* Replace custom cancellation action with `concurrency`

* Improve step names

... so don't have three steps with the same name

* Bump version of checkout action

checkout@v2 uses an old version of nodejs, which is deprecated.
2023-03-02 14:04:59 +00:00
Kegan Dougal 5c3d02cb89 sliding-sync: process receipt extension response
Needs some kind of tests, but this should do the trick.
2023-03-02 13:53:11 +00:00
Kévin Commaille e84a43dbaa Split StateStore integration tests into a trait
To declare the tests, the macro is still used but the content of the
tests is moved in the StateStoreIntegrationTests trait.

Allows to get the proper line reported when a panic occurs,
and have linting, formatting and IDE suggestions.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-03-02 14:35:51 +01:00
Richard van der Hoff 2fe08a90c5 Reinstate protoc for a couple of GHA jobs (#1607)
Looks like #1604 broke the build :(
2023-03-02 12:47:58 +00:00
Richard van der Hoff bdb9d274ce Skip installing protoc where it is unneeded (#1604)
This seems to have been cargo-culted to lots of places where it is redundant.
2023-03-01 22:17:36 +00:00
Ivan Enderlin 862b4a3df7 chore(bindings): Clean up and code simplification on matrix_sdk_ffi::sliding_sync
chore(bindings): Clean up and code simplification on `matrix_sdk_ffi::sliding_sync`
2023-03-01 20:12:45 +01:00
Ivan Enderlin 1dccea8735 doc: Fix a typo. 2023-03-01 19:59:55 +01:00
Ivan Enderlin e1fe1279c2 Merge pull request #1598 from Hywan/chore-sdk-sliding-sync-list-cleanup
chore(sdk): Various clean up on Sliding Sync (List)
2023-03-01 16:56:57 +01:00
Richard van der Hoff 15513b0ada Clean up the way we build xtask in CI (#1600)
Currently, the cache of the xtask binary isn't working terribly well:

* we seem to build it on each run anyway, presumably because we don't cache any of the intermediate build
   artifacts. Running the binary directly rather than indirecting via "cargo" prevents this.
* There is no sharing of the cache between the "rust" and "bindings" CI, because we use different cache keys.

This PR addresses both problems, and hopefully speeds up CI a bit as a result.
2023-03-01 15:50:31 +00:00
Jonas Platte c9e6d3e2dc Make RemoteEventTimelineItem fields private, provide methods instead 2023-03-01 16:45:43 +01:00
Jonas Platte 400879c819 Make LocalEventTimelineItem fields private, provide methods instead 2023-03-01 16:45:43 +01:00
Jonas Platte aac9d9c29b Move TimelineItemContent and related types into a new submodule 2023-03-01 16:45:43 +01:00
Jonas Platte 770a67678c Move RemoteEventTimelineItem into its own module 2023-03-01 16:45:43 +01:00
Jonas Platte 1452a2124f Move LocalEventTimelineItem into its own module 2023-03-01 16:45:43 +01:00
Jonas Platte daa3b80870 Re-export and document LocalEventTimelineItem, RemoteEventTimelineItem 2023-03-01 16:45:43 +01:00
Ivan Enderlin ccd9ad7b49 chore(bindings): Simplify the code.
Rust is a beautiful language. Let's use all the great constructions it gives to
us to write pretty code.
2023-03-01 16:42:47 +01:00
Ivan Enderlin 4212980f32 chore(bindings): Remove a From<JoinHandle> for TaskHandle impl.
This implementation is never used.
2023-03-01 16:42:09 +01:00
Ivan Enderlin 9b1c5079e6 chore: cargo fmt. 2023-03-01 15:58:22 +01:00
Ivan Enderlin a341caa773 chore(sdk): Continue the move from view to list in Sliding Sync. 2023-03-01 15:46:40 +01:00
Ivan Enderlin b4bf544676 test: Continue the move from view to list in Sliding Sync. 2023-03-01 15:43:59 +01:00
Ivan Enderlin bfb55d782f chore(sdk): Clean up rooms_ops. 2023-03-01 15:38:15 +01:00
Ivan Enderlin 3bad2a84ad chore(sdk): Remove SlidingSyncListRequestGenerator::live_request. 2023-03-01 15:17:53 +01:00
Ivan Enderlin 866c91ffbc chore(sdk): Simplify code of Sliding Sync request generator. 2023-03-01 15:17:31 +01:00
Ivan Enderlin c03ba55f24 chore(sdk): Rename InnerSlidingSyncListRequestGenerator to GeneratorKind. 2023-03-01 15:04:37 +01:00
Ivan Enderlin 1f5c4feafc chore(sdk): Extract Sliding Sync request generators into their own module. 2023-03-01 15:01:02 +01:00
Ivan Enderlin 6d2055449d chore(bindings): Continue to rename view to list in Sliding Sync. 2023-03-01 14:51:34 +01:00
Ivan Enderlin f5cc6feccd chore(sdk): Split the list.rs module into list/mod.rs and list/builder.rs. 2023-03-01 14:43:58 +01:00
Ivan Enderlin c5d5deba8e chore: Continue the renaming from view to list for Sliding Sync. 2023-03-01 14:39:09 +01:00
Ivan Enderlin d829fd3376 chore(bindings): Continue the renaming from view to list for Sliding Sync. 2023-03-01 14:38:43 +01:00
Ivan Enderlin 50a248c18b chore(sdk): Continue the renaming from view to list for Sliding Sync. 2023-03-01 14:38:26 +01:00
Ivan Enderlin c366eadf33 chore(sdk): Rename variables, clean up code, improve documentation… 2023-03-01 14:35:46 +01:00
Jonas Platte 318ede131d refactor(indexeddb): Use IndexeddbStateStoreError in StateStore impl 2023-03-01 14:30:13 +01:00
Jonas Platte 146db59370 refactor(base): Add an Error type to StateStore trait 2023-03-01 14:30:13 +01:00
Ivan Enderlin 97dd68e7fc fix(sdk): Deal with missing data in Sliding Sync's responses
fix(sdk): Deal with missing data in Sliding Sync's responses
2023-03-01 14:24:24 +01:00
Ivan Enderlin 4e4c745032 chore(sdk): Update all mentions to view as list in SlidingSyncList. 2023-03-01 14:20:44 +01:00
Ivan Enderlin f03767318f chore(sdk): Update all mentions to view as list in SlidingSync and SlidingSyncBuilder. 2023-03-01 14:18:40 +01:00
Ivan Enderlin cf83112bf0 chore(sdk): Move FrozenSlidingSyncList lower in the file. 2023-03-01 13:58:55 +01:00
Ivan Enderlin 575eea4701 fix(sdk): Deal with missing data in Sliding Sync's responses.
The Sliding Sync server might not send some parts of the response, because they
were sent before and the server wants to save bandwidth. So let's update values
only when they exist.
2023-03-01 12:19:30 +01:00
Ivan Enderlin 5377bb7ecf fix(sdk): Rename SlidingSyncView to SlidingSyncList.
The specification describes “list”, not “view”. Consistency is important, so
let's rename this type!
2023-03-01 12:09:34 +01:00
Ivan Enderlin 629b74e4f5 chore(sdk): Rename the sliding_sync::view module to …::list. 2023-03-01 11:56:01 +01:00
Ivan Enderlin 9475bb4d53 chore(indexeddb): Update version of uuid
chore(indexeddb): Update version of `uuid`
2023-03-01 09:09:15 +01:00
Ivan Enderlin 452588a18e chore(indexeddb): Update version of uuid. 2023-03-01 08:26:28 +01:00
Stefan Ceriu ff5dedbb07 fix(sliding_sync): Prevent PagingFullSync and GrowingFullSync from going over the total room count 2023-02-28 18:32:11 +01:00
valere 6539dc3060 quick typo 2023-02-28 17:53:54 +01:00
valere 7b8608e4c5 refactor(withheld) better type for no_olm 2023-02-28 17:38:37 +01:00
Stefan Ceriu 5c3972938e chore: Add platform versions for swift CI tests 2023-02-28 15:06:23 +01:00
Jonas Platte b936d3e32d chore: Remove workaround for clippy bug that was fixed 2023-02-28 12:36:08 +01:00
Kévin Commaille da8d74ccc1 feat(sdk): Keep track of events read receipts in the timeline API
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-28 09:44:35 +01:00
Kévin Commaille 6aad2a661d refactor(sdk): Rename ProfileProvider to RoomDataProvider
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-28 09:44:35 +01:00
Kévin Commaille 3139204f4b fix(examples): Don't use the sled store if the session is not persisted
Running the same example twice
borks encryption.
Instead point to an example that shows how to do it.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-28 09:20:53 +01:00
Kévin Commaille 1684dbe0a1 feat(examples): Add example to persist a session
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-28 09:20:53 +01:00
Doug 9531dc195a fix(bindings): Fix double quotes when replying to a reply. 2023-02-27 18:23:49 +01:00
Sam Wedgwood abab6dcf0f docs(crypto): Fix typo
Signed-off-by: Sam Wedgwood <sam@wedgwood.dev>
2023-02-27 15:39:01 +00:00
Ivan Enderlin d699161981 chore(sdk): Code reorganization of the sliding_sync module + doc improvement
chore(sdk): Code reorganization of the `sliding_sync` module + doc improvement
2023-02-27 15:27:08 +01:00
Ivan Enderlin 927c44bec3 doc(sdk): Fix doctests + remove mentions to futures-signals. 2023-02-27 15:08:13 +01:00
Ivan Enderlin 96248248ca chore(sdk): Move UpdateSummary at the bottom of the module. 2023-02-27 13:56:04 +01:00
Ivan Enderlin b7b86fa95b fix(sdk): Few fixes and cleans up of SlidingSync itself
fix+chore(sdk): Few fixes and cleans up of `SlidingSync` itself
2023-02-27 13:50:48 +01:00
Ivan Enderlin 4f7186a840 chore(sdk): Simplify the implementation of RoomListEntry. 2023-02-27 13:49:34 +01:00
Ivan Enderlin 9b062fa782 chore(sdk): Move RoomListEntry into the sliding_sync::view module. 2023-02-27 13:43:40 +01:00
Ivan Enderlin 4d8c3172a4 chore(sdk): Extract Error into its own module. 2023-02-27 13:42:01 +01:00
Ivan Enderlin b1c9ea9aa3 chore(sdk): Move SlidingSyncMode into the sliding_sync::view module. 2023-02-27 13:38:54 +01:00
Ivan Enderlin 49eee14665 chore(sdk): Move SlidingSyncState into the sliding_sync::view module. 2023-02-27 13:38:09 +01:00
Ivan Enderlin 6cec946401 chore(sdk): Improve documentation of SlidingSync. 2023-02-27 13:36:08 +01:00
Jonas Platte d06e8ccfc5 refactor: Upgrade eyeball and use SharedObservable
… but not in sliding sync yet, where some planned refactorings might
conflict with this.
2023-02-27 13:29:28 +01:00
Ivan Enderlin ac1d5afac3 doc(sdk): Improve inline documentation with link to an issue. 2023-02-27 13:29:03 +01:00
Ivan Enderlin f51b51b19e chore(sdk): Avoid mapping to a function returning (). 2023-02-27 12:14:01 +01:00
Ivan Enderlin 16c9845a47 chore(sdk): Rename SlidingSync.failure_count to .reset_counter.
This patch cleans up the `SlidingSync::stream` method. It renames variables,
improves log messages etc.

This patch also creates a new `MAXIMUM_SLIDING_SYNC_SESSION_EXPIRATION`
constant. This value was previously hardcoded and lost in the code, now it's
easier to spot it for further updates.

This patch finally renames the `failure_count` field to `reset_counter`,
because it doesn't count the number of failure, but the number of
`ErrorKind::UnknownPos` exactly, i.e. the number of times we reset the
`SlidingSync` state.
2023-02-27 12:09:14 +01:00
Ivan Enderlin 4dbd1a7db4 fix(sdk): Fix join in SlidingSync::sync_once.
This patch cleans up the `SlidingSync::sync_once` method by renaming variables,
adding more comments, simplifying the code by reducing the number of variables
etc.

This patch also changes `futures_util::join!` to `futures_util::future::join`.
It does the same but the macro needs the `async-await-macros` feature to be
turned on, while the second works without any features.

Finally, this patch improves the log messages by making them more clear for a
new reader.
2023-02-27 11:56:16 +01:00
Ivan Enderlin e5c85410a8 doc(sdk): Fix documentation, add links etc. 2023-02-27 11:54:48 +01:00
Ivan Enderlin 98ea3f096b chore(sdk): Clean up code of SlidingSync::cache_to_storage. 2023-02-27 11:54:13 +01:00
Anderas f0986643ce chore(crypto): Log Olm session identifiers
Co-authored-by: Damir Jelić <poljar@termina.org.uk>
2023-02-27 10:59:30 +01:00
valere f4d4d96c8e support migration for outbound after withheld addtition 2023-02-27 09:56:56 +01:00
Ivan Enderlin b214a0e098 feat(sdk): Remove clones of large responses in Sliding Sync
feat(sdk): Remove clones of large responses in Sliding Sync
2023-02-27 09:47:26 +01:00
Ivan Enderlin 70380a6ee4 doc(sdk): Improve documentation of SlidingSync::handle_response. 2023-02-27 09:30:50 +01:00
valere 39997376af fix js binding test 2023-02-25 16:33:16 +01:00
valere 58fa9d7767 fix ci 2023-02-25 16:18:06 +01:00
valere ad18ecc2c1 cleaning 2023-02-25 10:50:37 +01:00
valere ff500fc707 fix unused result 2023-02-25 10:39:17 +01:00
valere 76a286c91d feat(withheld) sled store support 2023-02-25 10:31:23 +01:00
valere 6cc31a0e13 fix clippy 2023-02-24 17:15:51 +01:00
valere 228b963f74 feat(withheld) fix indexeddb test 2023-02-24 16:39:37 +01:00
Jonas Platte 1783cd7738 test: Remove unused constant 2023-02-24 12:39:55 +01:00
Jonas Platte 32e8cb76b0 ci: Remove unused FeatureSet variant 2023-02-24 12:39:55 +01:00
valere 81c9f57e02 clippy fix 2023-02-23 18:02:45 +01:00
Benjamin Kampmann e162c99074 feat: Handy function to update power levels of a room 2023-02-23 17:53:48 +01:00
Benjamin Kampmann 6465398322 feat: expose power_levels.user_can_do on RoomMember 2023-02-23 17:53:48 +01:00
valere 6b948711b9 feat(withheld) Integration tests 2023-02-23 17:47:57 +01:00
Ivan Enderlin f803e58e36 chore(sdk): Keep to_remove inside its own scope. 2023-02-23 16:53:16 +01:00
Ivan Enderlin 23b3e3aa10 chore(sdk): Format code a little bit. 2023-02-23 16:40:48 +01:00
Ivan Enderlin fcfdf5c57c chore(sdk): Rename a variable in SlidingSync::handle_response. 2023-02-23 16:40:48 +01:00
Ivan Enderlin 3b01e4f9a6 chore(sdk): Use T::default instead of Default::default. 2023-02-23 16:39:56 +01:00
Ivan Enderlin 3c44f87bee doc(sdk): Simplify one documentation. 2023-02-23 16:39:39 +01:00
Ivan Enderlin af6cd85cf4 chore(sdk): SlidingSyncRoom::new no longer needs a mutable inner. 2023-02-23 16:27:19 +01:00
Ivan Enderlin 54654aa0c7 chore(sdk): Simplify references, thanks Clippy. 2023-02-23 16:03:11 +01:00
Ivan Enderlin 778d1211c7 feat(bindings): Replace many scripts by one xtask
Ganfra/kotlin binding scripts
2023-02-23 15:58:40 +01:00
Ivan Enderlin c69d28ef79 feat(sdk): Remove clones of large responses in Sliding Sync.
I admit this patch is quite tricky. Please try to follow me.

So, first off, in `SlidingSyncRoom::new`, we were clearing the timeline,
because somehow it exists twice in memory at this step.

Which led me to understand how `SlidingSync::handle_response` was working.
I've clarified how this part of the code works. We are dealing with 2 kind of
responses for a specific reason: `SyncResponse` and `v4::Response`, now it's
documented and I hope it's clearer.

Then, I notice that we were passing a clone of the entire sliding sync
response (`v4::Response`) to `Client::process_sliding_sync`. I thought it was
suboptimal, so I've updated the code to take a reference. It led me to update
`BaseClient::process_sliding_sync`. It was a little bit tricky, but I reckon we
have less clones now than before.

And now, back to `SlidingSync::handle_response`, I was able to compute the
`timeline` correctly by draining it from the `v4::Response`, or by moving it
from `SyncResponse`. So it's no longer necessary to have this clearing code
inside `SlidingSyncRoom::new`. Honestly it has nothing to do at this place
before.

To conclude: We have cleaner code, and less clones.

What thing I reckon could be optimized, is that the entire `timeline`
(`Vec<TimelineEvent>`) is cloned to be passed to `Client::handle_timeline`. So
this timeline exists in 2 places: in Sliding Sync, and somewhere else. I don't
believe it's a problem now, that's how it works, but we must be aware of that.
2023-02-23 15:21:21 +01:00
Jonas Platte d0c8ec7a22 refactor: Replace remaining usage of futures-signal with eyeball(-im) 2023-02-23 15:08:41 +01:00
Doug 01e3a31a11 fix(bindings): Verify entered homeserver before proxy availability
The reported errors weren't that helpful to the user.
Additionally attempt discovery when a URL is entered before falling back to the URL directly.
2023-02-23 12:12:53 +00:00
ganfra 83dcb0eb48 xtask kotlin : fix formatting 2023-02-23 11:53:32 +01:00
Jonas Platte 6c92bf8745 refactor(sdk): Remove unused dependency 2023-02-23 11:47:51 +01:00
Jonas Platte ba6fc794b0 refactor(base): Replace futures-signals with eyeball 2023-02-23 11:47:51 +01:00
Jonas Platte 17b86b7c38 refactor(crypto): Replace futures-signals with eyeball 2023-02-23 11:47:51 +01:00
valere 9894d3c72d feature(withheld) indexeddb store implem 2023-02-23 10:13:32 +01:00
boxdot d5154577a0 feat(sdk): Add custom login method
The `Client::login_custom` allows to login by using a custom login
method. In particular, it is possible to login to Synapse which supports
JWT authentication.

Signed-off-by: boxdot <d@zerovolt.org>
2023-02-23 08:16:04 +01:00
Ivan Enderlin 5be00d5950 chore(sdk): Many clean ups in SlidingSync
chore(sdk): Many clean ups in `SlidingSync`
2023-02-22 20:16:54 +01:00
Ivan Enderlin f3928e8d35 chore(sdk): Remove an unnecessary clone in SlidingSyncRoom::update. 2023-02-22 20:03:11 +01:00
Ivan Enderlin c766d8b3ef chore(sdk): Address PR feedback. 2023-02-22 20:00:49 +01:00
Ivan Enderlin 4d33fe1c31 chore(sdk): Rename some variable inside FrozenSlidingSyncRoom. 2023-02-22 19:59:57 +01:00
Ivan Enderlin e80713e933 chore(sdk): SlidingSyncRoom::update takes ownership of room_data.
First off, it's not necessary for `SlidingSyncRoom::update` to take a reference
to `room_data: v4::SlidingSyncRoom`. When `update` is called, the iterator owns
its items.

Second, by taking ownership of `room_data`, we no longer need to clone all the
data we need to assign to `self.inner`.
2023-02-22 19:59:57 +01:00
Ivan Enderlin 5d356ca1b5 feat(sdk): Avoid locking prev_batch in SlidingSyncRoom::timeline_builder. 2023-02-22 19:59:57 +01:00
Ivan Enderlin 56daa6cb8f chore(sdk): Rename SlidingSyncRoom pub(crate) from to pub(super) new.
First off, `SlidingSyncRoom.from` doesn't need to be visible to the entire
crate, only to `crate::sliding_sync.

Second, it's a constructor, so let's call it `new`.
2023-02-22 19:59:43 +01:00
Kévin Commaille bcc04bdf35 feat(sdk): Add conversion from EventTimelineItem and VirtualTimelineItem to TimelineItem
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-22 16:59:22 +00:00
Ivan Enderlin ebe8ce3177 feat(sdk): Make SlidingSync.pos already private
feat(sdk): Make SlidingSync.pos already private
2023-02-22 17:42:30 +01:00
Ivan Enderlin 1d02515186 chore(sdk): Re-organizing the code inside sliding_sync::room.
1. Put `FrozenSlidingSyncRoom` at the bottom of the module.
2. Put merge 2 `impl SlidingSyncRoom` together.
3. Remove the `AliveRoomTimeline` type alias.
4. Improve the documentation.
2023-02-22 17:38:37 +01:00
Ivan Enderlin 0abef77408 doc(sdk): Improve documentation of SlidingSyncBuilder. 2023-02-22 17:29:40 +01:00
Jonas Platte fe7f157253 refactor(bindings): Use UniFFI proc-macros for CrossSigningStatus 2023-02-22 17:21:21 +01:00
Ivan Enderlin 5a62ec3fc1 chore(ffi) Rename StoppableSpawn to TaskHandle
chore(ffi) Rename `StoppableSpawn` to `TaskHandle`
2023-02-22 17:11:36 +01:00
Ivan Enderlin 5727726e5d feat(sdk): Make SlidingSync.pos already private.
So far, the `SlidingSync.pos` field was public to the crate. In order to avoid
breaking the internal state of this type, its visibility is now private.

However, we need to be able to change the value when testing the
`SlidingSync` type itself. To achieve that, this patch removes the old
`force_sliding_sync_pos` function, and implements 2 new functions: `set_pos`
and `pos` directly on `SlidingSync` only when `#[cfg(any(test, feature ="testing"))]`.
2023-02-22 17:07:50 +01:00
Ivan Enderlin dec4b2122b doc(sdk): Improve documentation of SlidingSyncMode. 2023-02-22 16:51:42 +01:00
Ivan Enderlin cfcafc8425 chore(ffi) Rename StoppableSpawn to TaskHandle.
Because it is what it is.
2023-02-22 16:48:46 +01:00
valere 42934ea7e4 feature(withheld) ffi bindings 2023-02-22 15:46:56 +01:00
kegsay 7e64c15145 fix: look for the right list when waiting for updates
Otherwise it will time out after 30s and then continue executing,
causing a slow test.
2023-02-22 14:42:32 +00:00
valere e56d380f65 fix store after rebase 2023-02-22 14:28:20 +01:00
ganfra f72d9c2016 Merge branch 'main' into ganfra/kotlin_binding_scripts 2023-02-22 13:31:56 +01:00
Jonas Platte 95f1867816 chore: Delete sled-state-inspector
The sled store is on its way out and nobody is using this.
2023-02-22 13:17:01 +01:00
Jonas Platte b1062a67e0 fix(sdk): Rewrite decryption retrying to fix invalid index bug
Previously, it was possible for us to use invalid indices when:

- We retried decrypting multiple events at once
- One of them (not the last) was an edit or reaction

This lead to a situation where we would remove the UTD item once
decryption for it was successfully retried, but not account for the
resulting index shift for all later timeline items.
2023-02-22 12:44:11 +01:00
Janne Heß 02a213c1b5 feat(sdk): Add support to change display names of devices
Signed-off-by: Janne Heß <janne.hess@helsinki-systems.de>
2023-02-22 10:55:54 +00:00
Archit Bhonsle 64ec5ec561 feat(crypto): Expose and re-expose the version of the matrix-sdk-crypto crate 2023-02-22 10:21:48 +00:00
ganfra 953d4c0ef7 Remove kotlin bindings for now 2023-02-22 11:00:05 +01:00
Ivan Enderlin 1469e9033c feat(crypto-nodejs): OlmMachine.initialize takes a new optional store type
feat(crypto-nodejs): `OlmMachine.initialize` takes a new optional store type
2023-02-22 10:33:26 +01:00
ganfra 260e7c2d5e Update kotlin xtask to not build aar 2023-02-22 10:28:08 +01:00
Archit Bhonsle ae4ed2e7c8 feat(crypto): Re-expose the version of vodozemac 2023-02-22 09:45:25 +01:00
valere 28fadb875a Post rebase fix 2023-02-21 17:48:05 +01:00
valere 3dbad4229a feat(withheld): sql store support 2023-02-21 15:05:14 +01:00
valere c497efc27a feat(withheld): handle incoming withheld + report error 2023-02-21 15:04:58 +01:00
valere 0154cccd4b feat(withheld) Remember no_olm and implement in memory store 2023-02-21 15:03:04 +01:00
valere 99232f2340 refactor(withheld) 2023-02-21 15:00:34 +01:00
valere 592be5bd57 feat(withheld) basic no_olm 2023-02-21 15:00:34 +01:00
valere dc0fe35103 feat(withheld) balcklist + refactoring 2023-02-21 15:00:34 +01:00
valere 7accfdc2ef feat(withheld) Implement MSC2399 2023-02-21 15:00:34 +01:00
Damir Jelić 655d7d00d0 chore(crypto): Add support for encrypted m.dummy events
This mostly just silences an incorrect log warning that we received an unsupported event.

Co-authored-by: Jonas Platte <jplatte@matrix.org>
2023-02-21 14:33:14 +01:00
Jonas Platte ac5b439249 chore(sdk): Add an extra trace event to TimelineEventHandler::add 2023-02-21 12:11:03 +01:00
Jonas Platte f76bc16a10 fix(sdk): Lock timeline state once for decryption retrying
Before, we were releasing the lock and re-obtaining it between obtaining
item indices and using them for decryption retrying.
2023-02-21 12:11:03 +01:00
Jonas Platte a89a12701a refactor(sdk): Remove unneeded Arc around MutableVec 2023-02-21 10:20:11 +01:00
Jonas Platte 843fe6a239 refactor(sdk): Remove derive_builder 2023-02-21 10:20:11 +01:00
Jonas Platte cb06feea22 refactor(sled): Remove derive_builder
… and use owned Builder pattern like for Client.
2023-02-21 10:20:11 +01:00
Jonas Platte b0e7f3d031 refactor(indexeddb): Remove derive_builder
… and use owned Builder pattern like for Client.
2023-02-21 10:20:11 +01:00
Jonas Platte b96532878b refactor(sdk): Replace futures-signals with eyeball-im in the timeline 2023-02-20 18:01:31 +01:00
Ivan Enderlin b439d60b1f feat(sdk): SlidingSyncView.state, .rooms_list and .rooms_count are now private
feat(sdk): `SlidingSyncView.state`, `.rooms_list` and `.rooms_count` are now private
2023-02-20 16:25:02 +01:00
Ivan Enderlin e8af3e2d60 chore(clippy): Make Clippy happy. 2023-02-20 15:39:23 +01:00
Ivan Enderlin 1f3b8048c7 chore(jack-in): Use the latest SS API. 2023-02-20 15:39:23 +01:00
Ivan Enderlin 48257ac7cd test(sdk): SS integration test suites use the new API. 2023-02-20 15:39:23 +01:00
Matteo Ludwig dffc65007c feat(bindings): Add AudioInfo & AudioMessageContent to matrix-sdk-ffi
Signed-off-by: Matteo Ludwig <ludwig@silpion.de>
2023-02-20 14:01:06 +00:00
Ivan Enderlin 3796469a29 doc(sdk): Fix Sliding Sync examples. 2023-02-20 14:24:38 +01:00
Ivan Enderlin d49ceddfd5 feat(sdk): SlidingSyncView.state, .rooms_list and .rooms_count are now private.
Before this patch, `SlidingSyncView` has the following fields that were public:
`state`, `rooms_list` and `rooms_count`. Since they are `Mutable`, they can be
changed from the outside, and then will break the internal state of the view.
This problem is mentioned in https://github.com/matrix-org/matrix-rust-sdk/
issues/1474.

This patch solves this by making them prviate. Phew. That was simple!

But wait, we have a problem now. `matrix-sdk-ffi` was relying on them. So
this patch adds “helpers” methods on `SlidingSyncView`, like `state_stream`,
`rooms_list`, `rooms_list_stream`, `rooms_count` and `rooms_count_stream`.
Let's add new ones when it's necessary.
2023-02-20 14:22:42 +01:00
Ivan Enderlin 03c3f66c9a doc(sdk): Fix Sliding Sync examples. 2023-02-20 14:20:22 +01:00
Ivan Enderlin 7b06822687 chore(sdk): Group impl blocks for the same SlidingSyncView in a single impl block. 2023-02-20 14:15:20 +01:00
Jonas Platte 1f128c1dd6 refactor(sdk): Remove Mutable<_> around SlidingSyncView#sync_mode 2023-02-20 13:20:33 +01:00
Jonas Platte e92841d6bc refactor(sdk): Inline Mutable type aliases 2023-02-20 13:20:33 +01:00
Jonas Platte a112076664 refactor(sdk): Remove usage of MutableBTreeMap
No instance was ever subscribed to, so we can just use a regular shared RwLock.
2023-02-20 13:20:33 +01:00
Jonas Platte 2f9106a6d0 refactor(sdk): Make SlidingSync fields private 2023-02-20 13:20:33 +01:00
Ivan Enderlin 8a70df80b2 doc(sdk): Fix Sliding Sync module documentation.
doc(sdk): Fix Sliding Sync module documentation.
2023-02-20 12:23:10 +01:00
Jonas Platte cbcfd5d531 refactor(bindings): Fix SlidingSyncRoom timeline listener return type
It's an error if the room was not found, so it should be surfaced as such.
2023-02-20 12:10:13 +01:00
Ivan Enderlin b451460d8d doc(sdk): Improve Sliding Sync documentation
Co-authored-by: Jonas Platte <jplatte+git@posteo.de>
2023-02-20 12:06:20 +01:00
Ivan Enderlin 772a94cc8c doc(sdk): Fix Sliding Sync module documentation. 2023-02-20 11:49:35 +01:00
Jonas Platte 0f077ffcd3 fix(sdk): Don't overwrite parallel updates when fetching reply details 2023-02-20 11:15:23 +01:00
Jonas Platte 9eefe9377e refactor(sdk): Move timeline functionality into TimelineInner 2023-02-20 11:15:23 +01:00
Ivan Enderlin b35f2669d2 chore(sdk): Split the sliding_sync module into smaller ones
chore(sdk): Split the `sliding_sync` module into smaller ones
2023-02-20 11:09:05 +01:00
Ivan Enderlin 4102d25ebe chore(sdk): Split the sliding_sync module into smaller ones.
It's an strict copy-paste. Nothing has changed, except the
`SlidingSyncConfigBuilder::subscriptions` method that is now public within
the crate.
2023-02-20 10:43:05 +01:00
Ivan Enderlin 177fa140fc fix(sdk): FrozenSlidingSyncRoom.timeline_queue must serialize to timeline
fix(sdk): `FrozenSlidingSyncRoom.timeline_queue` must serialize to `timeline`
2023-02-17 19:46:50 +01:00
Ivan Enderlin e0eeba21ff fix(sdk): FrozenSlidingSyncRoom.timeline_queue must serialize to timeline.
In https://github.com/matrix-org/matrix-rust-sdk/pull/1478, I've introduced
a regression. The FrozenSlidingSyncRoom.timeline` field has been renamed to
`timeline_queue`.

I didn't notice that this type can be serialized and deserialized. That's
actually a breaking change because this type is stored somewhere once
serialized. So with #1478, it was impossible to deserialize them!

This patch fixes that. It also adds a test to ensure the serialization form of
`FrozenSlidingSyncRoom` doesn't change.
2023-02-17 19:24:39 +01:00
Jonas Platte e4f94f3174 chore: Fix warnings 2023-02-17 11:30:13 +01:00
Jonas Platte c2f529ed71 chore: Run cargo update
To pull in the latest Ruma release with a bugfix for content reporting.
2023-02-17 11:30:13 +01:00
Jonas Platte 0b91ec29a4 Allow to get or send any type of receipt 2023-02-17 10:54:03 +01:00
Kévin Commaille 362a394a11 feat(sdk)!: Allow to send private read receipts and receipts in threads
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-17 10:33:09 +01:00
Kévin Commaille 6ddb22d6ab feat(sdk): Expose methods to get a user or an event's receipts
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-17 10:29:22 +01:00
Kévin Commaille a3f289c6fc feat(base)!: Allow to get any receipt type for a user or event
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-17 10:29:21 +01:00
Jonas Platte 44d8f99bc4 docs(sdk): Remove outdated documentation 2023-02-17 09:56:29 +01:00
Ivan Enderlin eb3fb6986d doc(sdk): Fix links to examples and to matrix_sdk_base
doc(sdk): Fix links to examples and to `matrix_sdk_base`
2023-02-17 08:53:00 +01:00
Ivan Enderlin b6fb25fdfa test(sliding-sync): ensure unknown pos does recover the room subscription properly
test(sliding-sync): ensure unknown pos does recover the room subscription properly
2023-02-16 18:24:17 +01:00
Mauro 679d3084aa feat(bindings): Add Room::report_content 2023-02-16 17:18:18 +00:00
Ivan Enderlin 94b60f915f test(sdk): Address feedbacks. 2023-02-16 17:54:28 +01:00
Ivan Enderlin 3bf1060705 Merge branch 'main' into ben-fix-resubscribe-problem 2023-02-16 17:44:26 +01:00
Ivan Enderlin d600af7c0e feat(crypto-nodejs): OlmMachine.initialize takes a new optional store type.
This patch allows an `OlmMachine` to use a different store than Sled, like SQLite.

A new enum is introduced: `StoreType` which 2 variants: `Sled` (the default), and `Sqlite`.

The `OlmMachine.initialize` constructor now takes a new optional `store_type:
Option<StoreType>` argument. If no value is passed, the default `StoreType`
variant will be used (as mentioned: `StoreType.Sled`).

The code has been rewritten a little bit to make the type system happy without
introducing too much type indirections.

This patch finally adds a parameterized tests that exhaustively test: no store
type, `Sled` and `Sqlite`.
2023-02-16 16:26:06 +01:00
Damir Jelić 9649100adb feat(sdk): Add a root span to our client 2023-02-16 15:31:38 +01:00
Damir Jelić 292a9831f5 refactor(crypto): Move the usage specific cross signing types into the types module 2023-02-16 14:39:07 +01:00
Damir Jelić 04d8348477 refactor(crypto): Split out the cross signing key handling method 2023-02-16 14:39:07 +01:00
Ivan Enderlin 7049bd9183 doc(sdk): Improve Sliding Sync documentation
Sliding Sync API docs & minor fixes around API accessibility
2023-02-16 14:12:45 +01:00
Ivan Enderlin 1c7ffedf1a Merge branch 'main' into ben-sliding-sync-docs 2023-02-16 14:00:56 +01:00
Ivan Enderlin 7cc06cb76d chore(sdk): Small cleans up when reading the Sliding Sync code
chore(sdk): Small cleans up when reading the Sliding Sync code
2023-02-16 13:38:04 +01:00
Ivan Enderlin 9479f23f4b fix(sdk): Reconciliate events in the timeline queue in Sliding Sync when new updates arrive
fix(sdk): Reconciliate events in the timeline queue in Sliding Sync when new updates arrive
2023-02-16 12:35:35 +01:00
Ivan Enderlin c719d6d22e doc(sdk): Fix links to examples and to matrix_sdk_base. 2023-02-16 12:08:42 +01:00
Ivan Enderlin 6aa766efd0 chore(sdk): Address PR feedback. 2023-02-16 11:58:07 +01:00
Richard van der Hoff a0807cb337 crypto-js: support npm run build:dev (#1510)
Building the crypto-js bindings in release mode is very slow and not really
necessary for local development.

`--release` is the default, so there is no need to specify it
explicitly. Instead, allow `wasm-pack` args to be specified by an env var, and
add a `build:debug` npm script which will build in debug mode.
2023-02-16 10:51:37 +00:00
Richard van der Hoff 480800b996 crypto-js: have share_room_key return ToDeviceRequests (#1516)
We already have support for proper `ToDeviceRequest` objects in
`outgoing_requests`, so let's use it in `share_room_key` as well. It's much
easier for the application to work with the proper object than an untyped json
blob.
2023-02-16 10:51:15 +00:00
Ivan Enderlin 1c0288e42b chore(sdk): Simplify code by removing a map.
In the code `if let Some(global_data) = account_data.as_ref().map(|a|
&a.global)`, the `map` is not purely necessary. This patch simplifies the code
to remove the `map` and read the `global` field later on.
2023-02-16 11:49:21 +01:00
Ivan Enderlin 0a97ac8316 chore(sdk): Address PR feedback. 2023-02-16 11:45:10 +01:00
Ivan Enderlin 46006cf108 chore(sdk): Address PR feedback. 2023-02-16 11:43:25 +01:00
Ivan Enderlin 10adb6d26b chore(sdk): Use appropriate type to remove the need of a comment.
By using `Default::default` for the parameter value, we don't know which type
we are passing to the function. Hence the useful comment `room_info.ephemeral`.
This patch changes this to `Ephemeral::default`, so that we now know what we
pass without the need of a comment.
2023-02-16 11:23:05 +01:00
Richard van der Hoff 5abab932db ci: Fail coverage job if the coverage report can't be uploaded (#1515) 2023-02-16 09:36:43 +00:00
Damir Jelić abef9fae1a chore(bindings): Use tracing-android instead of android-logger 2023-02-16 09:59:16 +01:00
Ivan Enderlin b05d6874c0 test(sdk): Update the timeline inspection when the timeline_limit is modified. 2023-02-16 09:49:36 +01:00
Jonas Platte 3f2b12c996 chore(base): Remove unused futures-channel dependency 2023-02-16 08:59:41 +01:00
Ivan Enderlin 838488702f Merge branch 'main' into test-sliding-sync-timeline-limit-duplication 2023-02-16 08:45:22 +01:00
Ivan Enderlin 6c62b5f637 test(sdk): Restore the sliding-sync Docker image to v0.99.0. 2023-02-16 08:16:03 +01:00
Richard van der Hoff 826dc46ddf Merge pull request #1511 from matrix-org/rav/trace-to-debug
crypto-js: log trace messages at debug
2023-02-15 18:13:06 +00:00
Richard van der Hoff d9e455ab82 crypto-nodejs, crypto-js: prettify the javascript codebase (#1509) 2023-02-15 17:47:32 +00:00
kegsay 19b8ab409d test: Add depends_on block for the sliding sync proxy
This prevents the proxy starting before postgres is ready, which
causes the proxy to panic.
2023-02-15 17:32:57 +00:00
Richard van der Hoff 99b61ef660 Remove redundant log_trace 2023-02-15 17:06:46 +00:00
kegsay fb467b75e0 ci: Update sliding sync integration tests job 2023-02-15 17:03:29 +00:00
Jonas Platte 9e2fa13b9c fix(sdk): Respect new server ordering when receiving duplicate events 2023-02-15 17:40:16 +01:00
Jonas Platte 38e2d0bdcd fix(sdk): Fix day divider removal logic in timeline 2023-02-15 17:40:16 +01:00
Jonas Platte e8d5d29d2c chore(sdk): Improve tracing in TimelineEventHandler::add 2023-02-15 17:40:16 +01:00
Jonas Platte 88376e85cd test(sdk): Remove TestTimeline::with_initial_events
It's not general enough for further inital event tests.
2023-02-15 17:40:16 +01:00
Ivan Enderlin b1bfb868ab chore(sdk): Make Clippy happy. 2023-02-15 17:15:03 +01:00
Ivan Enderlin 6b3a550b22 chore(sdk): Fix lints. 2023-02-15 17:08:30 +01:00
Ivan Enderlin d9f9ed9d68 fix(sdk): Reconciliate timeline queue in SlidingSync when new updates arrive.
The code is self-documented. Please read it.
2023-02-15 16:50:40 +01:00
Jonas Platte 188aaecde3 feat(sdk): Keep Timeline event handlers around longer
… if the Timeline object itself is dropped, but something is still
subscribed to its changes.
2023-02-15 15:29:04 +01:00
Richard van der Hoff d41753ea17 crypto-js: log trace messages at debug
Javascript's `console.trace` is not a good equivalent for Rust's
`Level::TRACE`.

In particular:
 * `console.trace` causes a stacktrace to be written with each message
 * Under nodejs, `console.trace` writes the message to `stderr`, while
   `console.debug` writes to stdout

`console.trace` is therefore somewhat analogous to `console.error`.

Since we already include the log level in the message, we might as well just
send trace messages to `console.debug` rather than `console.trace`.
2023-02-15 12:50:42 +00:00
Richard van der Hoff e16c113db0 crypto-js, crypto-nodejs: Run prettier in CI 2023-02-15 12:39:54 +00:00
Richard van der Hoff 3f8590f86d crypto-nodejs, crypto-js: Run prettier on source code 2023-02-15 12:39:54 +00:00
Ivan Enderlin a0bd88aefc feat(crypto-nodejs) Implement OlmMachine.close
feat(crypto-nodejs) Implement `OlmMachine.close`
2023-02-15 12:48:45 +01:00
Jonas Platte 74056de8d7 fix(sdk): Deduplicate events coming from back-pagination 2023-02-15 12:41:26 +01:00
Jonas Platte 4f14728c91 chore(sdk): Add more spans and trace events to the timeline 2023-02-15 11:08:20 +01:00
Ivan Enderlin 57b810d70c test(sdk): Test changing the sliding sync limit on-the-fly. 2023-02-15 10:13:03 +01:00
Ivan Enderlin b3fedda90c chore(sdk): Clean up useless code.
The `experimental-timeline` feature is always enabled by `experimental-sliding-
sync`. Thus we can clean up the code.
2023-02-15 10:12:56 +01:00
Jonas Platte 20e082d416 refactor(sdk): Add a Date type for day divider logic 2023-02-15 10:08:05 +01:00
Jonas Platte 9e0ff4b9a9 fix(sdk): Handle remote echo on the first message of the day correctly 2023-02-15 10:08:05 +01:00
Jonas Platte b57bd55204 refactor(sdk): Use .rev().find_map() instead of .rfind().and_then() 2023-02-15 10:08:05 +01:00
Jonas Platte 4cad934a6b refactor(sdk): Simplify day divider timestamp conversion 2023-02-15 10:08:05 +01:00
Jonas Platte aa2b2dfff4 test(sdk): Fix reference to renamed test case 2023-02-15 09:31:42 +01:00
Jonas Platte 6c2a9dae55 test(sdk): Use u64 instead of u32 for next_ts
It seems weird to artificially limit the number range to 32 bits there,
panicking inside a test on too large values is fine.
2023-02-15 09:31:42 +01:00
Jonas Platte 360864841e test(sdk): Allow tests to set the next TS, use for day divider test 2023-02-15 09:31:42 +01:00
Jonas Platte 563ba884eb test(sdk): Make next_ts part of TestTimeline 2023-02-15 09:31:42 +01:00
Jonas Platte 13ffadc32a test(sdk): Add handle_live_redacted_message_event 2023-02-15 09:31:42 +01:00
Jonas Platte e246735866 test(sdk): Shorten some method names 2023-02-15 09:31:42 +01:00
Ivan Enderlin 6593f61556 doc(crypto-nodejs): Add safety comment for OlmMachine.close. 2023-02-15 09:15:31 +01:00
Jonas Platte 423d6ace83 fix(sdk): Attempt to EX-android crash with more hacks 2023-02-14 17:53:21 +01:00
Jonas Platte ce315a5229 chore: Use fully-qualified Docker image names
This brings us closer to being able to use our Docker setup with podman too.
2023-02-14 10:19:06 +01:00
Jonas Platte ca1163c9cc test: Remove trailing spaces from docker-compose.yml 2023-02-14 10:19:06 +01:00
Jonas Platte e576af8267 test: Fix docker-compose.yml error
Seems to be accepted by some older versions of docker-compose, but not
by current ones.
2023-02-14 10:19:06 +01:00
Jonas Platte 118497f9ec test: Remove unused dependency 2023-02-13 16:59:45 +01:00
Jonas Platte ac06172cf2 test(sdk): Add timeline items accessor 2023-02-13 16:21:30 +01:00
Damir Jelić 2654e909be feat(store-encryption): Add support to export/import the cipher using a key
This is useful for platforms which might want to avoid the cost of
password based key-derivation and have the ability to securely store an
encryption key.

Co-authored-by: Denis Kasak <dkasak@termina.org.uk>
2023-02-13 14:08:45 +00:00
Damir Jelić a1b1862479 docs(crypto): Explain a bit better what it means that a Device owns a room key
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-02-13 15:00:15 +01:00
Ivan Enderlin 5001cef372 Merge pull request #1375 from Hywan/feat-crypto-nodejs-requests-more-types 2023-02-13 14:46:47 +01:00
Kévin Commaille a6f74fa22c fix(sled): Use proper table name for encoding key when redacting
Contrary to other tables that use their own name to encode their keys,
the `ROOM_INFO` table uses the `ROOM` table name to encode its keys.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-13 14:07:06 +01:00
Damir Jelić 56cfa8f4f9 fix(crypto): Always mark your own device as verified 2023-02-13 14:01:31 +01:00
Richard van der Hoff 8d642784bb fix: remove dependency on broken wasm_timer crate
`wasm_timer` doesn't work outside the browser, which prevents it being used in
tests.

We can replace it with `gloo-timers` wich uses the standard Javascript
`setTimeout`.
2023-02-13 12:41:36 +01:00
enterprisey e45bf9f9bd docs(sdk): Improve read receipt/marker explanation 2023-02-13 12:05:35 +01:00
Jonas Platte 3a516a5fce refactor(sqlite): Use internal Error in CryptoStore impl 2023-02-13 12:05:04 +01:00
Jonas Platte 9fc7d1c78b refactor(indexeddb): Use IndexeddbCryptoStoreError in CryptoStore impl 2023-02-13 12:05:04 +01:00
Jonas Platte c6f0e47077 refactor(crypto)!: Add an error type to the CryptoStore trait
This makes it easier to implement it. However, using a different error
type than CryptoStoreError is a non-trivial change, so left for the
coming commits for all stores except the memory store.
2023-02-13 12:05:04 +01:00
Kévin Commaille 96c796bb0e ci: Silence clippy::extra_unused_type_parameters in affected crates
Triggers false positives.
See discussion in https://github.com/rust-lang/rust-clippy/issues/10319.
Possibly fixed in https://github.com/rust-lang/rust-clippy/pull/10321.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-13 11:57:05 +01:00
Kévin Commaille 63fa645cea fix(indexeddb): Fix implementation of SafeEncode for tuple of 5 elements
Regression introduced by merging commit 9707d73 after efdeba7

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-13 11:57:05 +01:00
Kévin Commaille 0d4fac5962 test(base): Test collision between threaded and unthreaded receipts in store
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-12 11:04:26 +01:00
Kévin Commaille 2e497198f8 fix(stores): Separate receipts by thread
The key encoding in key-value stores is backwards-compatible so
old receipts are counted as unthreaded receipts.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-12 11:04:26 +01:00
Kévin Commaille 9707d73ead fix(indexeddb): Fix broken raw redacted state events
A fix for (de)serialization of redacted state events in Ruma made
old events undeserializable.
Bump the DB version.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-11 22:54:38 +01:00
Kévin Commaille 5ae84a9a80 fix(sled): Fix broken raw redacted state events
A fix for (de)serialization of redacted state events in Ruma made
old events undeserializable.
Bump the DB version.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-11 22:54:38 +01:00
Jonas Platte 57c3224b6b ci: Work around not being able to use matrix variables inside uses 2023-02-10 13:58:41 +01:00
Jonas Platte 4a5a0293a9 ci: Replace unmaintained actions-rs/cargo action
… we really didn't need it and can use run instead.
2023-02-10 13:58:41 +01:00
Jonas Platte 3f16b324d7 ci: Use taiki-e/install-action to install cargo-tarpaulin 2023-02-10 13:58:41 +01:00
Jonas Platte a5bfab1d55 ci: Replace unmaintained actions-rs/toolchain action
… with dtolnay/rust-toolchain.
2023-02-10 13:58:41 +01:00
Jonas Platte e430f65ce8 ci: Exclude Debug implementations from coverage reports
We don't really want to check for exact representations, and otherwise
they are really just invoked in tests that fail.
2023-02-10 13:46:00 +01:00
Jonas Platte 441626bf68 ci: Exclude uniffi-bindgen from coverage checks 2023-02-10 13:02:31 +01:00
Jonas Platte 02e51a9cb9 ci: Test sqlite crypto store in coverage check 2023-02-10 13:02:31 +01:00
Jonas Platte 4492df6fc6 test(sdk): Add a test for retrying edit decryption 2023-02-10 12:41:05 +01:00
Jonas Platte 573b672470 fix(sdk): Fix handling of UTD "insivible" events
(ones which don't produce a timeline item on their own)
2023-02-10 12:41:05 +01:00
Jonas Platte fe3c73602f refactor(sdk): Always add UTD events to the timeline
… as currently this is the only way decryption can be retried.
2023-02-10 12:41:05 +01:00
Jonas Platte fd2dc18746 refactor(sdk): Retry event decryption in timeline item order
This works better for relations.
2023-02-10 12:41:05 +01:00
Jonas Platte 55549f61be test(sdk): Split timeline tests into smaller modules 2023-02-10 12:41:05 +01:00
Jonas Platte cf359dede5 chore: Upgrade rust-cache GitHub action 2023-02-10 12:16:06 +01:00
Ivan Enderlin 13f50fbf6c fix(sdk): Remove Deref impl for SlidingSyncRoom
fix(sdk): Remove `Deref` impl for `SlidingSyncRoom`
2023-02-09 16:32:31 +01:00
Ivan Enderlin 0f7646ade1 chore: Make Clippy happy. 2023-02-09 16:16:31 +01:00
Ivan Enderlin 4e1fe3bec4 chore: Fix a function renaming. 2023-02-09 15:49:55 +01:00
Ivan Enderlin c6d99305d5 fix(labs): Fix jack-in. 2023-02-09 15:46:08 +01:00
Ivan Enderlin c86ebaddd2 Update crates/matrix-sdk/src/sliding_sync.rs
Co-authored-by: Jonas Platte <jplatte+git@posteo.de>
2023-02-09 15:36:13 +01:00
Benjamin Kampmann 048d6a774c docs(sliding-sync): improved language
Co-authored-by: Ivan Enderlin <ivan@mnt.io>
2023-02-09 14:13:41 +01:00
Ivan Enderlin 43f2f60bfd fix(sdk): Remove Deref impl for SlidingSyncRoom.
This implementation is wrong in the sense of its semantics is not about
dereferencing a thin pointer to something, but just to give access to one
specific field of the entire structure. That's not how `Deref` is supposed to
be used.

Moreover, it creates conflict between the `SlidingSyncRoom.timeline` field,
and `SlidingSyncRoom.inner.timeline` field, which both exist, but not for the
same purposes. It creates confusion in the code.

Finally, it's better to expose proper getters to the outside world, so that we
control _and_ test _and_ know exactly what API we provide.
2023-02-09 13:09:22 +01:00
Gabriel Féron 12cbff2688 refactor(sdk): Use spawn_blocking when generating attachment thumbnails 2023-02-09 09:54:43 +01:00
Jonas Platte bdab40a772 refactor: Fix clippy lint str_to_string in sliding sync tests 2023-02-08 17:52:31 +01:00
Jonas Platte 87de9c7453 refactor(sled): Remove unused re-export 2023-02-08 17:52:31 +01:00
Benjamin Kampmann 765959758b docs(sliding-sync): Document cold-cache, reactive-api and bot-mode 2023-02-08 16:54:06 +01:00
Jonas Platte efdeba79f9 chore: Upgrade workspace dependencies 2023-02-08 16:04:29 +01:00
Jonas Platte e25061f8cb refactor(crypto)!: Unwrap unused Result 2023-02-08 16:04:29 +01:00
Jonas Platte 345f4f39f4 refactor(sqlite): Make Error more precise / useful 2023-02-08 15:44:17 +01:00
Jonas Platte e12b9e3027 refactor(sqlite): Encode olm hashes with rmp instead of serde_json
… like everything else in the sqlite crypto store.
2023-02-08 15:44:17 +01:00
Jonas Platte a22529344a refactor(sqlite): Inline inherent methods that duplicate trait methods 2023-02-08 15:44:17 +01:00
Jonas Platte 64b7c4eb59 refactor(sqlite): Make OpenStoreError more precise / useful 2023-02-08 15:44:17 +01:00
Jonas Platte 1988d1e9a3 refactor(sqlite): Move sqlite extension traits to utils module 2023-02-08 15:44:17 +01:00
Jonas Platte efc2397267 refactor(sqlite): Always use rusqlite::Error for sqlite extension traits 2023-02-08 15:44:17 +01:00
Jonas Platte ecb521205f refactor(sqlite): Move error types into new error module 2023-02-08 15:44:17 +01:00
Jonas Platte 7db40746bb refactor(sqlite): Start cleaning up cfg attributes 2023-02-08 15:44:17 +01:00
Benjamin Kampmann 3639215d53 docs(sliding-sync): long polling and quick refreshing explained 2023-02-08 13:29:38 +01:00
Benjamin Kampmann d51e9694f6 fix(sliding-sync): Set as default view mode 2023-02-08 12:43:32 +01:00
Jonas Platte ae21a56be2 chore: Add missing copyright headers 2023-02-08 11:15:02 +01:00
Kévin Commaille c252b984e0 refactor(sdk): Use a builder pattern to construct Timeline
`Timeline::with_fully_read_tracking` was public although all public
Timeline constructors enable it by default.
It is also meant to be called when constructing the timeline so
using a builder pattern makes sense.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-08 10:46:59 +01:00
Benjamin Kampmann 300fdec83f docs(sliding-sync): First half of sliding sync API docs 2023-02-07 22:41:58 +01:00
Jonas Platte ee9d47f647 refactor(crypto): Remove confusing explicit Sized bound
The bound is there by default, no need to put it in explicitly.
2023-02-07 18:45:32 +01:00
Jonas Platte 2e80073f28 refactor(crypto): Use IntoCryptoStore for OlmMachine::with_store 2023-02-07 18:45:32 +01:00
Jonas Platte c3d5932a0f refactor(crypto): Move store::{CryptoStoreError, Result} into new module 2023-02-07 18:45:32 +01:00
Doug 7fdccfcea4 chore(bindings): Rename property and use feature flag. 2023-02-07 15:22:39 +00:00
Doug 630836b3c7 chore(bindings): Store the proxy URL directly in Client. 2023-02-07 15:22:39 +00:00
Doug 3705925a22 chore(bindings): Persist the sliding sync proxy.
Added to the Session for automatic restoration.
2023-02-07 15:22:39 +00:00
Doug b7a3ab7c5b feat(ffi): Sliding sync proxy discovery. 2023-02-07 15:22:39 +00:00
Jonas Platte 5692480112 refactor(crypto): Remove unused method 2023-02-07 15:59:19 +01:00
Jonas Platte 5b63bd4cf1 refactor(crypto): Reduce direct visibility of some internal types
They weren't being exported from any public modules before.
This helps human readers as well as some compiler lints.
2023-02-07 15:59:19 +01:00
Jonas Platte 9711e1b2f5 refactor(crypto): Move (Into)CryptoStore traits into a new module 2023-02-07 15:59:19 +01:00
Damir Jelić a17346158f docs(sdk): Fix the sliding sync builder example 2023-02-07 14:26:33 +01:00
Damir Jelić 30ddbab686 refactor(sdk): Move the bulk of the sliding sync logic into a sync_once method 2023-02-07 14:26:33 +01:00
Jonas Platte b436918cb3 fix(sqlite): Fix Debug output for SqliteCryptoStore 2023-02-06 17:08:21 +01:00
Jonas Platte b20424b8b5 chore(sqlite): Undo weird formatting of TOML file 2023-02-06 17:08:21 +01:00
Ivan Enderlin 5f933b1033 feat(sdk): Simplify code by using std::cmp::min
feat(sdk): Simplify code by using `std::cmp::min`
2023-02-06 14:46:46 +01:00
Ivan Enderlin 5e8224f19c feat(sdk): Simplify code by using std::cmp::min. 2023-02-06 13:43:01 +01:00
Damir Jelić e68134dd53 chore(crypto): Improve the logs for the OlmMachine constructor 2023-02-06 13:31:00 +01:00
Damir Jelić 945c16a7fb feat(crypto): Throw an error if our user/device pair isn't what we have in the store 2023-02-06 13:31:00 +01:00
dependabot[bot] fc8cd2e7e5 chore(deps): bump tokio from 1.24.1 to 1.24.2
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.24.1 to 1.24.2.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/commits)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-04 08:27:50 +01:00
Damir Jelić 4115975135 fix(bindings): Expose the OpenTelemetry logging for other platforms as well 2023-02-03 22:30:11 +01:00
Damir Jelić 35a26be7b0 chore(sdk): Record the sliding sync pos in our logs 2023-02-03 16:51:33 +01:00
Jonas Platte 052832b4d3 fix: Don't log raw events that fail deserialization
… but do log relevant parts to allow debugging.
2023-02-03 14:35:10 +01:00
Richard van der Hoff e516e15d2b matrix-sdk-crypto-js v0.1.0-alpha.4
Release `crypto-js` / Publish 🕸 [m]-crypto-js (push) Failing after 31s
2023-02-03 12:17:37 +01:00
Ivan Enderlin 2bc9407563 feat(sdk): Remove SlidingSyncView.rooms
feat(sdk): Remove `SlidingSyncView.rooms`
2023-02-03 11:51:25 +01:00
Jonas Platte 47f8b6cb79 feat(ffi): Add Room::fetch_members 2023-02-02 17:48:26 +01:00
Jonas Platte e11dd099d8 feat(sdk): Add Timeline::fetch_members 2023-02-02 17:48:26 +01:00
Jonas Platte c294a376e8 refactor(sdk): Use TimelineDetails for sender profile 2023-02-02 17:48:26 +01:00
Jonas Platte ffa0190517 refactor(sdk): Add missing feature-gate
… for fn that is only used from sliding-sync.
2023-02-02 17:48:26 +01:00
Jonas Platte 3a863fa8fb refactor(sdk): Remove unnecessary inner block in function 2023-02-02 17:48:26 +01:00
ganfra 75d396e2ec Merge branch 'main' into ganfra/kotlin_binding_scripts 2023-02-02 17:10:34 +01:00
Ivan Enderlin 672a640854 chore(labs): Update to later changes. 2023-02-02 15:27:18 +01:00
Ivan Enderlin 40010cd511 Merge branch 'main' into feat-sliding-sync-view-rooms 2023-02-02 15:26:30 +01:00
Ivan Enderlin a6d33fcd16 feat(labs): Update jack-in to the new sliding sync room API. 2023-02-02 15:22:41 +01:00
Ivan Enderlin d08606e3b9 feat(sdk): SlidingSync::get_room now takes a ref to OwnedRoomId
feat(sdk): `SlidingSync::get_room` now takes a ref to `OwnedRoomId`.
2023-02-02 14:06:14 +01:00
Ivan Enderlin ae8a8c6cc3 feat(sdk): SldingSync::get_room takes a &RoomId. 2023-02-02 13:36:32 +01:00
Ivan Enderlin 0d87d0f786 feat(sdk): Remove SlidingSyncView.rooms.
`SlidingSyncView` exposes a `room` field. Problem: It's not updated
correctly and leads to error. The canonical way to get a room is by using
`SlidingSync::get_room`. This patch thus removes `SlidingSyncView.rooms`
entirely.
2023-02-02 13:31:46 +01:00
Ivan Enderlin a9ba2dd546 feat(sdk): SlidingSync::get_room now takes a ref to OwnedRoomId.
Before this patch, `SlidingSync::get_room` was taking ownership of an
`OwnedRoomId`, to only use it as a reference. Thus, calling this method was
creating useless clones.

This patch updates `SlidingSync::get_room` to receive a reference to
`OwnedRoomId`.
2023-02-02 13:25:36 +01:00
Kévin Commaille 00638ba74c feat(sdk): Allow to fetch replied to messages
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-02-02 13:06:11 +01:00
ganfra 6a7b28c085 Merge branch 'main' into ganfra/kotlin_binding_scripts 2023-02-02 11:52:32 +01:00
ganfra 1bcd8c7aae Comment remove gradle_build 2023-02-02 11:50:47 +01:00
Jonas Platte ce973b35e9 chore: Upgrade uniffi to 0.23.0 2023-02-02 10:15:05 +01:00
Jonas Platte 8a1b1eccd6 doc(sdk): Fix docs that refer to TimelineKey 2023-02-02 10:15:05 +01:00
Jonas Platte e068b4987e doc(sdk): Document what happens when timeline event sending fails 2023-02-02 10:14:23 +01:00
Jonas Platte 3a1eb62c38 refactor(sdk): Expose event sending errors through timeline item
… instead of through the return value of Timeline::send.
2023-02-02 10:14:23 +01:00
Jonas Platte c8021cf2ba refactor(sdk): Move LocalEventTimelineItem#event_id into send_state 2023-02-02 10:14:23 +01:00
Jonas Platte a48fd77c4a refactor(sdk): Rename LocalEventTimelineItemSendState => EventSendState 2023-02-02 10:14:23 +01:00
Damir Jelić ab0e27622e feat(bindings): Add support to setup a OpenTracing based logger 2023-02-01 22:55:47 +01:00
Damir Jelić 7b044ef5dd fix(crypto): Ignore the usage and signatures when comparing cross signing keys
Co-authored-by: Denis Kasak <dkasak@termina.org.uk>
2023-02-01 18:25:48 +01:00
Benjamin Kampmann 8a9b72ae6c test(sliding-sync): ensure unknown pos does recover the room subscription properly 2023-02-01 17:37:16 +01:00
Anderas e9cef35f99 Add matrix-sdk-sqlite with a CryptoStore implementation
Note about "Write-Ahead Log" (WAL) mode: The SQLite WAL mode has a
bunch of advantages that are quite nice to have:

1. WAL is significantly faster in most scenarios.
2. WAL provides more concurrency as readers do not block writers and a
   writer does not block readers. Reading and writing can proceed
   concurrently.
3. Disk I/O operations tends to be more sequential using WAL.
4. WAL uses many fewer fsync() operations and is thus less vulnerable
   to problems on systems where the fsync() system call is broken.

The downsides of WAL mode don't really affect us. So let's turn it on.

More info: https://www.sqlite.org/wal.html

Co-authored-by: Jonas Platte <jplatte@matrix.org>
Co-authored-by: Damir Jelić <poljar@termina.org.uk>
2023-02-01 15:06:59 +01:00
Damir Jelić 3a183b4c22 chore(sdk): Tweak the instrumentation for some encryption related methods 2023-02-01 10:16:14 +01:00
Richard van der Hoff 5dbee7dcdc fix(indexeddb): correctly encode Olm session keys
When we store an Olm session in the database, we encode the sender key and
session ID using the store cipher. That means that we also need to encode them
when we look them up, otherwise we won't find them.
2023-01-31 16:59:22 +01:00
Benjamin Kampmann b9b00743c9 fix(sliding-sync): catching up on new count for full-sync views 2023-01-31 15:02:19 +00:00
Benjamin Kampmann 14be1f888a test(sliding-sync): integration test for UnknownPos 2023-01-31 15:02:19 +00:00
Benjamin Kampmann a77295120a fix(sliding-sync): fixing restarting growing view 2023-01-31 15:02:19 +00:00
Benjamin Kampmann a6ad0e35b3 style(sliding-sync): improved style and docs 2023-01-31 14:03:43 +00:00
Benjamin Kampmann 5b3ec33ddc chore: Update to specific, supported ruma and sliding-sync-proxy-version. 2023-01-31 14:03:43 +00:00
Benjamin Kampmann aa10e70851 feat(sliding-sync): enable gzip when enabling sliding-sync 2023-01-31 14:03:43 +00:00
Benjamin Kampmann 826b2874ec ffi: expose sliding sync extension enabling 2023-01-31 14:03:43 +00:00
Benjamin Kampmann 8f17b6c38d feat(sliding-sync): add Receipt and Typing extension config 2023-01-31 14:03:43 +00:00
Benjamin Kampmann 9ae9c98340 test(sliding-sync): activate live views test 2023-01-31 14:03:43 +00:00
Benjamin Kampmann f6da282557 feat(sliding-sync): add delta-token support 2023-01-31 14:03:43 +00:00
Benjamin Kampmann 9ad34e8565 chore(sliding-sync): update to latest sliding-sync JSON layout 2023-01-31 14:03:43 +00:00
Benjamin Kampmann a973c372e6 chore: use local volumes for testing server 2023-01-31 14:03:43 +00:00
Ivan Enderlin 00e93e08e7 feat(sdk): Add EventTimelineItem::unique_identifier. 2023-01-31 13:21:19 +01:00
Kévin Commaille 75a2d2d92c fix(sdk): Aggregate reactions locally
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-01-31 12:11:44 +00:00
Jonas Platte e066346b16 feat(bindings): Clear the timeline when sliding-sync loop is reset 2023-01-31 11:49:25 +01:00
Jonas Platte 754453c135 refactor: Import tracing macros instead of using qualified paths 2023-01-31 11:49:25 +01:00
Jonas Platte c481caf060 refactor: Clean up format strings 2023-01-31 11:49:25 +01:00
Jonas Platte 6d868908cb refactor(sdk): Remove unused Result 2023-01-31 11:49:25 +01:00
Jonas Platte d2678e2f01 refactor(sdk): Remove unused async 2023-01-31 11:49:25 +01:00
Jonas Platte c24316e5e3 refactor(sdk): Remove unused lifetime parameter 2023-01-31 11:49:25 +01:00
Jonas Platte 48bc5b9b7e refactor(sdk): Work around receiving event with txn-id multiple times 2023-01-30 20:12:44 +01:00
Jonas Platte 01c828ed4b chore(sdk): Remove weird space 2023-01-30 20:12:44 +01:00
Benjamin Kampmann 102994b030 feat(sliding-sync): allow updating the timeline limit at runtime 2023-01-30 16:05:19 +00:00
Ivan Enderlin 38dfa90441 feat(sdk): LocalEventTimelineItem has a new send_state field
feat(sdk): `LocalEventTimelineItem` has a new `send_state` field
2023-01-30 14:08:47 +01:00
Ivan Enderlin 274bc20603 doc(sdk): Fix typos. 2023-01-30 13:58:00 +01:00
Ivan Enderlin c8d561d17d feat(ffi): Add LocalEventTimelineItemSendState + EventTimelineItem::local_send_state. 2023-01-30 13:51:29 +01:00
Ivan Enderlin 8ea2cef55d feat(sdk): LocalEventTimelineItem has a new send_state field.
This patch is trying to resolve the following issue:

When a local event is sent to the server, in case of an error, the
`Timeline::send` method just returned the error but it wasn't reflected inside
the `LocalEventTimelineItem` type itself. It's easy to loss this information.

So now, there is a new enum: `LocalEventTimelineItemSendState` with 3 states:

1. `NotSentYet`, when the local event has not been sent yet, it's theorically
   the initial state,
2. `SendingFailed`, when the local event has been sent but it's failed!
3. `Sent`, when the local event has been sent successfully.

Therefore, the `LocalEventTimelineItem` struct has a new field: `send_state`.

The send state is managed by its `with_event_id` method which now receives an
`Option<OwnedEventId>` (prev. `OwnedEventId` directly):

* If it's `Some(_)`, then it means we got a successful response from the server
  after the sending, so the send state is set to `Sent`,
* If it's `None`, then it means we got an error response from the server, so
  the send state is set to `SentFailed`.

(A small change: The method `TimelineInner::add_event_id` has been renamed
`TimelineInner::update_event_id_of_local_event`.)

The `Timeline::send` still returns a `Result`, but the server's response is
passed to a new method: `TimelinerInner::handle_local_event_send_response`
(it's logically named after the `handle_local_event` method), which is
responsible to call `TimelineInner:update_event_id_of_local_event` (which was
previously called directly by `Timeline::send`).

And `TimelineInner::update_event_id_of_local_event` was already calling
`LocalEventTimelineItem::with_event_id`. So everything works like a charm here.

The local send state is closely managed by `LocalEventTimelineItem`, I hope it
will avoid state breakage as much as possible.
2023-01-30 11:47:38 +01:00
Ivan Enderlin 4086492ef9 feat(sdk): EventTimelineItem is now an enum … { Local(…), Remote(…) }
feat(sdk): `EventTimelineItem` is now an `enum … { Local(…), Remote(…) }`
2023-01-30 11:03:49 +01:00
Ivan Enderlin 6bfa3df691 chore(sdk): Replace ref by &. 2023-01-30 10:20:44 +01:00
Ivan Enderlin f4607146e2 chore(sdk): Replace ref by &. 2023-01-30 10:13:54 +01:00
Ivan Enderlin 0a7193de62 chore(sdk): Remove useless custom Debug impl. 2023-01-30 09:30:12 +01:00
Ivan Enderlin 5c2e6b2743 chore(sdk): Simplify code. 2023-01-30 08:58:26 +01:00
Ivan Enderlin 47256eb4f9 chore(sdk): Remove a warning for an unused variable. 2023-01-30 08:50:47 +01:00
Ivan Enderlin 1c8d57321c chore(sdk): Remove Flow.timestamp and .raw_event. 2023-01-30 08:50:08 +01:00
Ivan Enderlin 322aa776f2 feat(sdk): Remove LocalEventTimelineItem.encryption_info and .raw, and Remote….raw is no more an Option. 2023-01-30 08:49:48 +01:00
ganfra 517b71cb06 kotlin bindings: replace shell by xtask scripts 2023-01-27 16:29:44 +01:00
Ivan Enderlin 17a9c4829d chore(sdk): Make Clippy happy. 2023-01-27 15:25:37 +01:00
Ivan Enderlin 59a5805af7 chore(sdk): Remove commented code. 2023-01-27 15:14:10 +01:00
Ivan Enderlin 882f3cad25 chore(sdk): Simplify code by using Into<EventTimelineItem> for LocalEventTimelineItem`. 2023-01-27 15:12:38 +01:00
Ivan Enderlin 5ce3c45637 chore(sdk): Simplify code by importing a type. 2023-01-27 15:07:42 +01:00
Ivan Enderlin ab617ddda0 chore(sdk): Fix a typo in an error message. 2023-01-27 15:05:27 +01:00
Ivan Enderlin 3a1c5580e7 chore(sdk): Fix a typo in an error message. 2023-01-27 15:03:49 +01:00
Ivan Enderlin 8486976804 feat(ffi): Update EventTimelineItem::reactions to return an Option. 2023-01-27 14:59:47 +01:00
Ivan Enderlin 8f13bb5ca5 feat(ffi): Add the EventTimelineItem::is_local and is_remote method. 2023-01-27 14:59:27 +01:00
Ivan Enderlin 7257cff5fe docs: Add the ffi scope. 2023-01-27 14:58:54 +01:00
Ivan Enderlin eb47869a45 feat(ffi): Remote TimelineKey. 2023-01-27 14:58:34 +01:00
Ivan Enderlin 636f4f8f8d feat(sdk): Remove the TimelineKey type. 2023-01-27 14:41:01 +01:00
Damir Jelić 819f962419 fix(sdk): Retry decryption if you initialize a Timeline with some events
The sliding sync logic has another room type called `SlidingSyncRoom`.

This room type stores a limited amount of events in something called
`AliveRoomTimeline` in a in-memory cache. This room also gets persisted
to the store via another room type called `FrozenSyncRoom`.

These types are used when clients restore their view of the room, i.e.
when the user enters the room to look at messages.

When the client enters a room, a `Timeline` object will be created, but
it will be initially populated with events coming from
`AliveRoomTimeline`.

In a hot potato contest of who should be responsible to decrypt injected
events, everybody claims to be allergic to potatoes.

This patch modifies the `Timeline` constructor that populates the
timeline with events from the `AliveTimeline` to retry decryption on the
events that the `AliveTimeline` pushes into the `Timeline`.

Note that, because the `AliveRoomTimeline` never replaces encrypted
events with decrypted ones, every time the client enters/exits the room
events well get re-decrypted.
2023-01-27 14:07:41 +01:00
Ivan Enderlin 3c9e84397a test(sdk): Update tests since the new EventTimelineItem type. 2023-01-27 14:00:41 +01:00
Ivan Enderlin f7bf6ba220 feat(sdk): Rewrite EventTimelineItem as an enum: Local and Remote. 2023-01-27 13:26:31 +01:00
Denis Kasak 539cbee087 test(crypto): Test failure when deserializing cross-signing keys with incorrect usage. 2023-01-26 17:01:00 +01:00
Damir Jelić d442cac936 refactor!(crypto): Directly deserialize into per usage cross signing key type
This moves the usage checking directly into our deserialization path.
2023-01-26 17:01:00 +01:00
Damir Jelić 72ed024303 fix(crypto): Make sure the master key self-signs as well
This is an omission from the spec where the subkeys are supposed to be
signed by the master key, but signing the master key wasn't mentioned.

This is still fine, since we always treated the master key and their
subkeys as a whole and never accepted one without the others.
2023-01-26 17:01:00 +01:00
Damir Jelić 148c4907b5 fix(crypto): Inspect the key usage when receiving cross signing keys 2023-01-26 17:01:00 +01:00
Ivan Enderlin 8c91314b58 feat(sdk): Move EventTimelineItem.event_id into TimelineKey::TransactionId
feat(sdk): Move `EventTimelineItem.event_id` into `TimelineKey::TransactionId`
2023-01-26 15:07:40 +01:00
Ivan Enderlin 9006bd1bba chore(sdk): Address review feedback. 2023-01-26 14:55:16 +01:00
Kévin Commaille 32e7209c66 refactor(sdk): Split room member events between membership and profile change
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-01-26 14:24:59 +01:00
Ivan Enderlin 4dfa41ce8f doc(sdk): Simplify if let into let
doc(sdk): Simplify `if let` into `let`
2023-01-26 14:24:35 +01:00
Ivan Enderlin 1a5ead58f6 feat(sdk): Move EventTimelineItem.event_id into TimelineKey::TransactionId.
To create an event next to the server, the event must be accompanied by
a transaction ID. When the server receives the event creation request, it
responds with a new event ID (which we will name `event_id_1`). Later on, when
a sync is done to the server, the server may respond with the transaction ID
and a new event ID (which we will name `event_id_2`).

The transaction ID is used to update the state of the local event. The event
ID received from the sync may ideally be the same as the one received by the
creation request's response.

Knowing that…

`EventTimelineItem` had a field: `event_id: Option<OwnedEventId>`. It was
`Some(event_id)` where `event_id` is the event ID responded by the server when
an event was created, i.e. it's `event_id_1`; otherwise it was `None`. This is
never `event_id_2`. Why?

Because `EventTimelineItem` has an other field: `key: TimelineKey`, which
represents the following states:

* `TimelineKey::TransactionId(OwnedTransactionId)`,
* `TimelineKey::EventId(OwnedEventId)`, where the event ID is `event_id_2`!

So it becomes obvious that `event_id_1` should be part of `TimelineKey`, not
`EventTimelineItem`. `TimelineKey` is where this transaction ID and event ID
logic is managed.

This patch updates `TimelineyKey::TransactionId` to be defined as:

* `TimelineKey::TransactionId { txn_id: OwnedTransactionId, event_id: Option<OwnedEventId> }`.

Why an `Option`? Because before receiving `event_id_1`, we may still want to
create a `TimelineKey`, the API must reflect that state.

So basically, `EventTimelineItem.event_id` is moved inside
`TimelineyKey::TransactionId`.
2023-01-26 11:36:42 +01:00
Jonas Platte 220617fd04 fix(sdk): Update add_event_id logs to make more sense 2023-01-26 10:59:28 +01:00
Jonas Platte a37ce68eae fix(sdk): Only look for local echoes for events appended to the timeline
… rather than prepended, or added at a specific point as replacement for
an existing timeline item.
Previously, we would log lots of useless tracing events when retrying to
decrypt UTD items.
2023-01-26 10:55:41 +01:00
Ivan Enderlin 0c4ca84eec test(sdk): Improve m.fully_read test coverage and documentation
test(sdk): Improve `m.fully_read` test coverage and documentation
2023-01-26 10:16:48 +01:00
Ivan Enderlin 77b7451f59 doc(sdk): Fix a typo. 2023-01-26 10:06:11 +01:00
Ivan Enderlin bac105a9aa doc(crypto-nodejs): Fix a typo. 2023-01-26 09:59:15 +01:00
Ivan Enderlin 832bba8a2b test(sdk): Improve m.fully_read test coverage and documentation. 2023-01-26 09:46:47 +01:00
Jonas Platte 0e39a7cbf2 chore: Upgrade Ruma 2023-01-25 18:01:12 +01:00
Jonas Platte db11940f8f test(sdk): Fix clippy warning 2023-01-25 18:01:12 +01:00
Damir Jelić 4ef654f1d1 chore: Bump the Ruma version 2023-01-25 18:01:12 +01:00
Ivan Enderlin 793e18e44f Merge pull request #1388 from matrix-org/release-matrix-sdk-crypto-js-v0.1.0-alpha.3
Release matrix-sdk-crypto-js v0.1.0-alpha.3
2023-01-25 12:56:41 +01:00
Ivan Enderlin 578b446ea4 chore(sdk): Rename a variable to clarify its content. 2023-01-25 11:00:03 +01:00
Ivan Enderlin 3fe5f13df4 doc(sdk): Simplify if let into let. 2023-01-25 10:58:46 +01:00
Ivan Enderlin 54f00dc05a feat(crypto-nodejs) Implement OlmMachine.close.
This patch fixes https://github.com/matrix-org/matrix-rust-sdk/issues/1379/.

This is similar to https://github.com/matrix-org/matrix-rust-sdk/pull/1197/.
We need to close the `OlmMachine`. Problem, `napi-rs` doesn't allow methods to
take ownership of `self`, so the following code:

```rs
fn close(self) {}
````

isn't allowed. There an [`ObjectFinalize`](https://docs.rs/napi/latest/napi/bindgen_prelude/trait.ObjectFinalize.html)
trait, but it's only called when the JS value is being collected by the GC.
It's not possible to force call `finalize` here.

This patch then introduces a new type:

```rs
enum OlmMachineInner {
  Opened(matrix_sdk_crypto::OlmMachine),
  Closed
}
```

that derefs to `matrix_sdk_crypto::OlmMachine` when its variant is `Opened`,
otherwise it panics. Calling the new `OlmMachine::close` method will change the
inner state from `Opened` to `Closed`.

Ideally, I wanted to throw an error instead of panicking, but `napi_env`
(used to build an `Env`, used to throw an error) is neither `Sync` nor `Send`.
Honestly, my knowledge of NodeJS and NAPI is too weak to implement `Sync`
and `Send` for `*mut napi_env__` safely. Especially because it can be executed
from various threads within `async fn` functions, that are driven by Tokio in
`napi-rs`. So, yeah, for the moment, it panics!
2023-01-25 09:56:21 +01:00
Benjamin Kampmann 464e40bc32 fix(ffi): add erroneously removed loop 2023-01-24 15:41:12 +00:00
Benjamin Kampmann f22660d5e9 chore(sliding-sync): more logging information 2023-01-24 15:41:12 +00:00
Andy Uhnak 2db1bc75c1 Expose MissingRoomKey decryption error 2023-01-24 16:27:02 +01:00
Damir Jelić 18ec101719 refactor!(crypto): Move some caches out of the CryptoStore implementations
This moves the caches we have for tracked users out of the Sled, and
IndexedDB, CryptoStore into the Store struct. The Store struct is a
wrapper for the CryptoStore trait, so this is the natural place where
these caches should live.

Co-authored-by: Jonas Platte <jplatte@matrix.org>
2023-01-24 15:26:33 +00:00
Doug a762b2027f feat(bindings): Expose aliases to the FFI. 2023-01-24 16:12:39 +01:00
Jonas Platte 934181be44 refactor(sdk): Update confusing variable names 2023-01-24 13:43:55 +01:00
Jonas Platte 3d34743ae9 fix(sdk): Respect server ordering for remote echoes 2023-01-24 13:43:55 +01:00
Kévin Commaille 718864bfdd refactor(sdk): Provide day divider as a timestamp
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-01-24 12:29:15 +01:00
Benjamin Kampmann 24ac5b9f16 feat(sliding-sync): add configuration to inform about room changes without positional update 2023-01-24 10:30:07 +00:00
Benjamin Kampmann cc835da347 fix(sliding-sync): refresh timeline if server told us we are limited 2023-01-24 10:30:07 +00:00
Benjamin Kampmann 933eec2d1d fix(sliding-sync): switch Atomic Ordering from Relaxed to SeqCst 2023-01-24 10:30:07 +00:00
Benjamin Kampmann de4a8e3e7a chore(base): additional tracing information for sliding sync responses 2023-01-24 10:30:07 +00:00
Benjamin Kampmann b08c740181 fix(ffi): don't cancel the actual spawn, instead ensure it finishes processing before stopping the loop 2023-01-24 10:30:07 +00:00
Benjamin Kampmann 950bb83f8d style: use trace! instead of tracing::trace! 2023-01-24 10:30:07 +00:00
Benjamin Kampmann a06aa43dd1 feat(sliding-sync): cache room data next to view for selective unfreezing 2023-01-24 10:30:07 +00:00
Benjamin Kampmann a572f61edf tests: additional ignored tests for future scenarios 2023-01-24 10:30:07 +00:00
Benjamin Kampmann a964585e35 fix(sliding-sync): have views go live right at response processing 2023-01-24 10:30:07 +00:00
Benjamin Kampmann 71407a636c fix(sliding-sync): use single replace on cold and empty views
Before we'd push one empty entry at a time, which lead to a lot of vecdiff updates pushed. With this
we now only push one  event after the entire operation set has been applied on the fresh
or cold view.
2023-01-24 10:30:07 +00:00
Benjamin Kampmann b7a8a93a73 chore(sliding-sync): additional tracing around unfreezing from cold cache 2023-01-24 10:30:07 +00:00
Damir Jelić 600fd62dc5 crypto(fix): Fail to decrypt if there's a key mismatch
A mismatch between the recorded Ed25519 and Curve25519 keys of the room
key and the identity keys of a Device used to be just a warning.

Considering that many clients will aggressively try to hide E2EE related
warnings let's upgrade this clear error into a full blown decryption
failure.

Co-authored-by: Denis Kasak <dkasak@termina.org.uk>
2023-01-24 09:14:42 +01:00
Damir Jelić 00d1437e5a chore(crypto): Log the full error messages when decrypting room events 2023-01-23 15:03:03 +01:00
Damir Jelić f774159e15 chore(crypto): Record the event id when decrypting a room event 2023-01-23 15:03:03 +01:00
Damir Jelić a1fba86f75 chore(crypto): Log the session id when we receive a room key 2023-01-23 15:03:03 +01:00
Damir Jelić 3c4d1cfb47 fix(sled): Don't load the tracked users with the Account
We might have a lot of tracked users, clients will usually have 1-10
thousand tracked users. This might slow down client startup if there are
so many tracked users.

We don't need the tracked users until we sync or try to send a message,
so load them lazily when they are first needed.
2023-01-23 13:35:51 +01:00
Damir Jelić 3ab95c74c7 refactor!(crypto): Make the methods related to tracked users async 2023-01-23 13:35:51 +01:00
Damir Jelić 8c1a81eebc chore: Fix the conventional commit type for documentation
The conventional commit specification recommends the "docs" type for
documentation related changes. This also matches the initial usage of
this type in our commit history.

While the default git-cliff config does specify a regex "^doc", where I
suspect our types have been taken from, this regex actually allows "doc"
as well as "docs" for the type.

Nevertheless, let's specify that we're using the one recommended in the
spec.
2023-01-20 20:30:06 +01:00
Richard van der Hoff acfa8a4190 docs(bindings): Fix return type documentation for get_missing_sessions 2023-01-20 20:09:42 +01:00
Richard van der Hoff 0771d73543 matrix-sdk-crypto-js v0.1.0-alpha.3
Release `crypto-js` / Publish 🕸 [m]-crypto-js (push) Failing after 46s
2023-01-20 17:37:49 +00:00
Richard van der Hoff 92546acc85 chore(crypto-js): configuration for 'yarn version' 2023-01-20 17:37:40 +00:00
Richard van der Hoff c7f258d8b0 doc: Improve the documentation of "tracked users"
I had no idea what a tracked user was, so I've attempted to clarify this
somewhat for future readers.

Co-authored-by: Damir Jelić <poljar@termina.org.uk>
2023-01-20 14:02:02 +01:00
Jonas Platte 1e2fd02478 doc(base): Explain purpose of BaseClient#crypto_store 2023-01-20 10:56:09 +01:00
Stefan Ceriu b0c70dc606 chore(bindings): Exclude the target folder from the debug swift package
… to improve package loading time.
2023-01-20 09:00:36 +00:00
Benjamin Kampmann 0fea33de4d feat(ffi): expose server_versions on ClientBuilder 2023-01-19 21:31:57 +00:00
Damir Jelić 053b609899 chore(sdk): Add a prefix to the request id to make it easier to grep for them 2023-01-19 15:47:08 +01:00
Damir Jelić a54a421f91 chore(sdk): Log the response/request sizes as well 2023-01-19 15:46:49 +01:00
Ivan Enderlin 78147e0d33 doc(crypto-js): Fix wording. 2023-01-18 17:22:02 +01:00
Ivan Enderlin 430c573494 feat(crypto-nodejs): Request types have more fields.
Instead of putting all request fields inside a single `body` JSON-
encoded string  field, there is now more types. There is less need to call
`JSON.parse`, and the type system will be able to catch more things.

For example, `ToDeviceRequest` now has 2 more fields: `event_type` and
`txn_id`.
2023-01-18 17:21:52 +01:00
Ivan Enderlin 9db09e22b2 feat(crypto-js): Request types have fields extracted from their “body” directly
feat(crypto-js): Request types have fields extracted from their “body” directly
2023-01-18 13:41:14 +01:00
Ivan Enderlin 94fadf74b9 feat(crypto-js): Rename RoomMessageRequest.content to .body. 2023-01-18 13:31:26 +01:00
Ivan Enderlin 7c01f8490f test(crypto-js): Test `RoomMessageRequest.content. 2023-01-18 13:14:06 +01:00
Ivan Enderlin ca00112804 test(crypto-js): Add test for in-room verification (w/ RoomMessageRequest). 2023-01-18 11:42:34 +01:00
Benjamin Kampmann 6c0ef2db51 refactor(ffi): refactor stoppable spawn to be more token-oriented 2023-01-18 10:08:58 +00:00
Benjamin Kampmann 938a03867b feat(ffi): add subscribe_and_add_timeline_listener helper fn
re-applying the fixes from ##1346
2023-01-18 10:08:58 +00:00
Ivan Enderlin 90cce1a47c chore(crypto-js): Improve documentation, and do a little clean up. 2023-01-18 10:58:59 +01:00
Ivan Enderlin c427caf473 feat(crypto-js): Rename requests extra field to body. 2023-01-18 10:58:59 +01:00
Ivan Enderlin 3ad675db5c feat(crypto-js): Extract RoomMessageRequest.event_type from EventContent. 2023-01-18 10:58:59 +01:00
Ivan Enderlin efde81646b feat(crypto-js): Extract RoomMessageRequest.content as a JSON-encoded string. 2023-01-18 10:58:59 +01:00
Ivan Enderlin 8f9859c41c feat(crypto-js): Rename the body fields to extra to avoid confusion with HTTP terminology. 2023-01-18 10:58:58 +01:00
Ivan Enderlin 6248e2bc68 test(crypto-js): Update tests according to previous commit. 2023-01-18 10:58:58 +01:00
Ivan Enderlin c0ccd55df8 feat(crypto-js): request! differentiates “extracted” and “grouped” fields. 2023-01-18 10:58:58 +01:00
Ivan Enderlin b125b2e9da test(crypto-js): Update tests according to previous commit. 2023-01-18 10:58:58 +01:00
Ivan Enderlin 3c838b2f4a feat(crypto-js): Request types have fields from their “body” directly. 2023-01-18 10:58:58 +01:00
Jonas Platte f4fba3efb7 fix(bindings): Add user_id to RoomMembership
Without that, it is useless.
2023-01-18 09:58:36 +01:00
Jonas Platte e64b2f65da Revert "refactor(bindings): Fetch latest event earlier"
This reverts commit 7d1372195cf2af56c3d8ef95ac1b183ace8a5eec.
2023-01-18 09:31:45 +01:00
Doug 9e12a88e03 feat(bindings): Allow setting the store passphrase in the bindings
Co-authored-by: Damir Jelić <poljar@termina.org.uk>
2023-01-18 06:45:21 +00:00
Anderas 0af38d9a76 fix(crypto): Mark our own identity as verified if we have the private part of the identity
Co-authored-by: Denis Kasak <dkasak@termina.org.uk>
Co-authored-by: Damir Jelić <poljar@termina.org.uk>
2023-01-17 18:41:00 +00:00
Damir Jelić ade3a09848 chore(base): Remove a leftover unwrap 2023-01-17 19:13:29 +01:00
Damir Jelić 3641e0d540 docs(crypto): Note that the in-room verification events need to be decrypted 2023-01-17 18:11:53 +01:00
Damir Jelić e1d8c72d3a chore(crypto): Improve some docs
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2023-01-17 18:11:53 +01:00
Damir Jelić c8d9581f8b refactor!(crypto): Don't process in-room verification implicitly 2023-01-17 18:11:53 +01:00
Kévin Commaille 8cc632ffd1 feat(sdk): Add support for state events in the timeline
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-01-17 12:10:19 +01:00
Kévin Commaille 1b7f4b4b42 fix(test): Fix position of prev_content in JSON
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-01-17 12:10:19 +01:00
Damir Jelić 928871d16e chore(sdk): Log the request body for sliding sync requests 2023-01-16 17:02:46 +01:00
Damir Jelić 87f5b251f5 chore(sdk): Add a request id counter to our HTTP client
This solves the issue of correlating different log lines that are talking
about the same request/response cycle. Plaintext logs would show this as
a flat structure, while something like Jaeger would show you a nice
tree. This allows you to reconstruct the tree in plaintext logs.
2023-01-16 17:02:46 +01:00
Damir Jelić e76272a68b chore(sdk): Improve the HTTP request sending logs 2023-01-16 17:02:46 +01:00
Damir Jelić b30c74cb01 chore(crypto): Bump the to-device event logging to debug 2023-01-16 16:23:58 +01:00
Jonas Platte 686541155f feat(sdk): Add display_name_ambiguous to timeline::Profile 2023-01-16 15:35:29 +01:00
Jonas Platte b256b6f625 refactor(bindings): Fetch latest event earlier
Does this crash?
2023-01-16 15:35:29 +01:00
Jonas Platte 7b913a0759 feat(bindings): Add EventTimelineItem::sender_profile 2023-01-16 15:35:29 +01:00
Jonas Platte 9e60a9dd3f feat(sdk): Attach profile information to timeline items 2023-01-16 15:35:29 +01:00
Jonas Platte 00bdb08973 refactor(sdk): Move locking of timeline_items into handle_remote_event 2023-01-16 15:35:29 +01:00
Jonas Platte ed70ccb988 refactor(sdk): Add trait ProfileProvider for use in TimelineInner 2023-01-16 15:35:29 +01:00
Jonas Platte e52af8a315 refactor(sdk): Make TimelineInner fields private 2023-01-16 15:35:29 +01:00
Jonas Platte b5450ee5ad test(sdk): Simplify TestTimeline construction 2023-01-16 15:35:29 +01:00
Jonas Platte b105b34cf6 refactor(sdk): Use regular struct update syntax for EventTimelineItem
The previous solution was annoying to maintain and only more efficient
without compiler optimizations.
2023-01-16 15:35:29 +01:00
Jonas Platte e0b0b632ec chore: Fix a typo 2023-01-16 11:31:03 +01:00
Jonas Platte 4febe45364 refactor(base): Make AmbiguityChange(s) non-exhaustive
They are meant to be consumed, but not created, by other crates.
2023-01-16 11:31:03 +01:00
Jonas Platte 70de11a73a refactor: Remove unused Deserialize, Serialize implementations 2023-01-16 11:31:03 +01:00
Jonas Platte 58f2d53293 doc(sdk): Update spec link 2023-01-16 11:31:03 +01:00
Doug 6540b9bb49 feat(bindings): Expose read receipts to FFI 2023-01-16 10:58:40 +01:00
Jonas Platte 6c53842599 chore: Bump ruma, vodozemac 2023-01-16 10:34:06 +01:00
Jonas Platte 663d043abb refactor: Always use Debug for logging enum or identifier values 2023-01-16 10:34:06 +01:00
Damir Jelić 386cbce344 Revert "feat(ffi): add subscribe_and_add_timeline_listener helper fn"
This reverts commit ba4e304cad.
2023-01-13 14:55:39 +01:00
Damir Jelić ece9252a3d chore(crypto): Add the sender and event type to the to-device handling span 2023-01-13 12:46:07 +01:00
Damir Jelić c13cb4957e chore(crypto): Don't use a JsOption needlessly. 2023-01-13 12:46:07 +01:00
Damir Jelić 1f34a016e5 chore(crypto): Add the to-device message id to the logs if there is one 2023-01-13 11:58:42 +01:00
Damir Jelić b8dd704658 refactor(crypto): Move the to-device event handling into a separate method 2023-01-13 11:58:42 +01:00
Benjamin Kampmann 273e96265f Merge pull request #1335 from gnunicorn/ben-sliding-sync-integration-test
Various smaller fixes on sliding-sync for elem-x:

 - Infrastructure and smoke test only for integration testing sliding-sync
 - Expose Pop and Clear on VecDiff
 - Expose adding views during sliding sync live runtime incl integration tests
 - bug fix on replacing only in-window-items in the insert-code (was leading to a wrong list setting), including integration test
2023-01-13 10:11:30 +00:00
Jonas Platte 0e3ea58aa4 Revert unrelated change 2023-01-13 10:51:52 +01:00
Benjamin Kampmann ba4e304cad feat(ffi): add subscribe_and_add_timeline_listener helper fn 2023-01-13 08:09:10 +00:00
Benjamin Kampmann 723e3a3135 tests(sliding-sync): minor style improvements 2023-01-12 19:53:57 +01:00
Kévin Commaille 1c754e663d fix(sdk): Make pagination strategy implement Send
Required when `PaginationOptions` must implement `Send`,
e.g. with `tokio::runtime::Runtime::spawn`.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-01-12 19:24:47 +01:00
Damir Jelić 846844e67b fixup! chore(sdk): Re-export the AmbiguityChange type 2023-01-12 19:12:01 +01:00
Damir Jelić 51d96213ba chore(sdk): Re-export the AmbiguityChange type
This was forgotten to be re-exposed after it was moved to the base
crate.
2023-01-12 19:12:01 +01:00
Damir Jelić 80868919ae fix(crypto): Make the device update logic a bit more strict and obvious
When updating a device after we receive new device keys from a
/keys/query call we implicitly check that the user and device id match
since we need to fetch the device using the mentioned ids.

This moves the check into the update method as well to make it more
explicit. We also prevent upgrades of the Ed25519 key since we don't
support any different key type anyways.
2023-01-12 17:55:06 +01:00
Damir Jelić e3e9898f88 fixup! test(crypto): Check that we sign and verify fallback keys properly 2023-01-12 17:53:22 +01:00
Damir Jelić f2055f8069 test(crypto): Check that we sign and verify fallback keys properly 2023-01-12 17:53:22 +01:00
Damir Jelić 151f87a417 chore(crypto): Improve some docs
Co-authored-by: Denis Kasak <dkasak@termina.org.uk>
2023-01-12 17:37:06 +01:00
Damir Jelić 0e4f4d69b8 feat(crypto): Handle the failures field in the /keys/claim response 2023-01-12 17:37:06 +01:00
Damir Jelić 8db84a3c9e feat(crypto): Handle the failures field in the /keys/query response 2023-01-12 17:37:06 +01:00
Damir Jelić 1b18402707 feat(crypto): Add a TTL-cache that will handle failures for some requests 2023-01-12 17:37:06 +01:00
Benjamin Kampmann 6ec8ff6363 ci(coverage): ignore new sliding-sync-integration-tests for now 2023-01-12 13:51:59 +01:00
Benjamin Kampmann 906c09987f style(sliding-sync): minor style refinements 2023-01-12 13:24:16 +01:00
Benjamin Kampmann c93a9ef9a3 fix(sliding-sync): Simply room_range checking
Co-authored-by: Jonas Platte <jplatte+git@posteo.de>
2023-01-12 13:24:16 +01:00
Benjamin Kampmann 51f2e773a0 fix(ffi): add Pop and Clear to VecDiff for sliding-sync 2023-01-12 13:24:16 +01:00
Benjamin Kampmann 3c6c5f4fae ci: disable broken sliding-sync-integration-test-runs 2023-01-12 13:24:16 +01:00
Benjamin Kampmann a8d49b86df feat(sliding-sync): adding views on live sliding-sync 2023-01-12 13:24:16 +01:00
Benjamin Kampmann 2949dcc773 docs: Add a Readme to explain usage 2023-01-12 13:24:16 +01:00
Benjamin Kampmann b790bf9397 test(sliding-sync): add integration test covering #1333 2023-01-12 13:24:16 +01:00
Benjamin Kampmann 91941a5360 fix(sliding-sync): ensure we are only replacing items in the requested range 2023-01-12 13:24:16 +01:00
Benjamin Kampmann fabf0d40bc test: add test for moving window and rooms 2023-01-12 13:24:16 +01:00
Benjamin Kampmann 08bec243ff ci(sliding-sync): add sliding sync integration test 2023-01-12 13:24:16 +01:00
Benjamin Kampmann 60d82fde38 test: add sliding-sync integration-testing facilities 2023-01-12 13:24:16 +01:00
Jonas Platte 866158f6ff chore(sdk): Add logging for when timeline events are not handled 2023-01-11 17:14:09 +01:00
Kévin Commaille f883293168 chore: Update Ruma
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2023-01-11 16:05:02 +01:00
Damir Jelić c6e883da68 fix(sdk): Pass in to-device events to the OlmMachine unconditionally
The sliding sync proxy seems to have started to omit the e2ee extension
completely if no changes happened to the one-time key counts.

Our sync response processing assumed that, if there are some to-device
events that need to be passed to the OlmMachine, then we'll have some
data in the e2ee extension of the response as well. In other words, it
wouldn't pass in the to-device events to the OlmMachine if the e2ee
field of the sync response is `None`.

Since the assumption isn't true, we're going to pass in default values
for the one-time key counts when the e2ee extension field of the
response is `None`.
2023-01-11 14:18:40 +01:00
Damir Jelić cdfb51a606 refactor!(sdk): Add the experimental prefix to the sliding-sync feature 2023-01-11 11:54:32 +01:00
Damir Jelić 3e88905518 chore(sdk): Make the timeline feature a dependency of the sliding sync one 2023-01-11 11:54:32 +01:00
Damir Jelić 156c351023 chore(crypto): Instrument the receive_sync_changes method
This is mainly done so we create a span with the name of the function,
since we can filter logs by the name of the span we're now able to
enable logs for this method only.
2023-01-11 11:17:58 +01:00
Jonas Platte a98c1fd00d chore: Update Cargo.lock 2023-01-10 10:19:02 +01:00
Jonas Platte f4bfbdf97d chore: Upgrade base64 2023-01-10 10:19:02 +01:00
Jonas Platte 2e30e11101 refactor: Use workspace dependencies for more crates 2023-01-10 10:19:02 +01:00
Jonas Platte 3af6ba245c refactor: Fully replace matches crate with assert_matches 2023-01-10 10:19:02 +01:00
Benjamin Kampmann db1d31c9cc fix(sliding-sync): Do not implictly activate to-device extension upon unfreeze
Previously, when we found a since-token in the frozen state, we'd always activate the to-device extensions
regardless of whether the user had activated it in their builder or didn't. With this change we are only
setting the since parameter from cache if the user actually activated the to-device extension.
2023-01-10 10:04:31 +01:00
Jonas Platte 444a82fd9e fix(sdk): Small documentation update 2023-01-10 10:01:26 +01:00
Jonas Platte 6e218bdebc feat(sdk): Make pagination more flexible and smarter
- Don't actually fire off a request when the top of the timeline has
  already been reached
- Allow making multiple requests without removing the loading indicator
  in between
2023-01-10 10:01:26 +01:00
Jonas Platte 9b3bf5a4fa feat(sdk): Add TimelineStart virtual timeline item 2023-01-10 10:01:26 +01:00
Jonas Platte 237edcd747 refactor(sdk): Move day_divider constructor to TimelineItem 2023-01-10 10:01:26 +01:00
Jonas Platte 271e925adc refactor(sdk): Add and use TimelineItem::{read_marker, is_read_marker} 2023-01-10 10:01:26 +01:00
Benjamin Kampmann 514530c19a fix(sliding-sync): ensure last index is also invalidated
Index ranges are inclusive, but our loop would stop one short. This particuarly
tricky when the selective view is moved, as we didn't properly invalidate all items.
2023-01-09 22:48:57 +00:00
Benjamin Kampmann 58aec0d126 feat(uniffi): Add support to set list filters on sliding sync view builders (#1296)
* feat(uniffi): Add support to set list filters on sliding sync view builders
* fix: expose SlidingSyncRequestListFilters via proc-macros
2023-01-09 14:45:35 +00:00
dependabot[bot] 63c8696cac chore(deps): bump tokio from 1.22.0 to 1.23.1
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.22.0 to 1.23.1.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.22.0...tokio-1.23.1)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-07 14:06:32 +01:00
Damir Jelić 6aff4fc2c0 Update the bindings to use the SAS signalling (#1300)
* feat(bindings): Expose support for manually starting SAS verifications

Co-authored-by: Stefan Ceriu <stefanc@matrix.org>
2023-01-06 10:20:40 +01:00
Jonas Platte 8375c47f42 feat(sdk): Add pagination loading indicator as a virtual timeline item 2023-01-06 09:48:44 +01:00
Jonas Platte 1b2387bcb2 refactor(sdk): Enforce no pagination overlap in timeline
… by locking the pagination token for the entire duration of the API call.
2023-01-06 09:48:44 +01:00
Jonas Platte 1e71036b19 refactor(sdk): Simplify day divider matching 2023-01-06 09:48:44 +01:00
Jonas Platte e0f5dc97a5 refactor(bindings): Avoid single letter type aliases 2023-01-05 10:53:56 +01:00
Jonas Platte 64f367d474 feat(bindings): Expose all TimelineItemContent variants 2023-01-05 10:53:56 +01:00
Jonas Platte bb39c2aa9d feat(sdk): Persist sliding-sync to-device extension since value 2023-01-04 19:05:37 +01:00
Jonas Platte b82cd7fa5a refactor(common): Simplify wasm timeout implementation
… and remove an infinitely-recursive From impl in the process.
2023-01-04 18:59:11 +01:00
Jonas Platte 6780dc0312 refactor(sdk): Increase default request timeout to 30s 2023-01-04 17:55:18 +01:00
Damir Jelić 72519b7386 chore(labs): Use clap instead of structopt for jack-in
Update to clap v4 in jack-in
2023-01-04 12:49:50 +01:00
Jonas Platte c7b41e3fca test(sdk): Add a test for TimelineInner::add_initial_events 2023-01-04 12:44:51 +01:00
Marcel Kräml 5852d751bd Updated jack-in README.md for full-sync-mode lowercase options
Signed-off-by: Marcel Kräml <m.kraeml@kraeml.it>
2023-01-04 12:38:54 +01:00
Marcel Kräml cda596ebed Changed full_sync_mode to lowercase
Signed-off-by: Marcel Kräml <m.kraeml@kraeml.it>
2023-01-04 12:21:14 +01:00
Jonas Platte a5e17be3a1 refactor(bindings): Only create a timeline for SlidingSyncRoom if needed 2023-01-04 11:44:07 +01:00
Jonas Platte 33cdc108b5 chore(sdk): Log number of events in add_initial_events 2023-01-04 11:44:07 +01:00
Jonas Platte c3dfd2e744 refactor(sdk): Only lock timeline items if needed in add_initial_events 2023-01-04 11:44:07 +01:00
Jonas Platte 8ab5bb7fca refactor(sdk): Use HttpError::client_api_error_kind 2023-01-04 11:44:07 +01:00
Jonas Platte 80d8a8f41f refactor: Import tracing macros 2023-01-04 11:44:07 +01:00
Jonas Platte 46fe998a33 refactor(crypto): Improve logging 2023-01-03 13:09:09 +01:00
Jonas Platte 7efbba5d8b refactor(sdk): Improve logging in timeline::event_handler 2023-01-03 13:09:09 +01:00
Jonas Platte 907d50f773 refactor(sdk): Move TimelineInnerMetadata definition to timeline::inner 2023-01-03 13:09:09 +01:00
Jonas Platte 9cba98ae1b feat(sdk): Add num_updates to timeline PaginationOutcome 2023-01-03 09:44:23 +01:00
Jonas Platte 108950c706 refactor(sdk): Remove update_timeline_item
… inlining it into its only call site.
2023-01-03 09:44:23 +01:00
Jonas Platte 4452e15489 refactor(sdk): Rename TimelineEventHandler#event_added to item_created 2023-01-03 09:44:23 +01:00
Matthew Hodgson b026d90bcf fix(sdk): Handle to-device sliding sync extension correctly
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2023-01-02 18:00:38 +00:00
Jonas Platte aa5f74ffb1 fix(sdk): Fix clippy lint 2023-01-02 12:45:50 +01:00
Jonas Platte 469c6cf104 fix(sdk): Don't log raw JSON which can contain personal data 2023-01-02 12:45:50 +01:00
Jonas Platte 3c5b005aae fix(sdk): Remove message contents from TimelineItem debug string 2023-01-02 12:45:50 +01:00
Marcel Kräml eef0bf4b91 Fixed clap for jack-in and update README.md
Signed-off-by: Marcel Kräml <m.kraeml@kraeml.it>
2022-12-23 11:29:21 +01:00
Marcel Kräml ad1011ec53 Replace structopt with clap in jack-in
Signed-off-by: Marcel Kräml <m.kraeml@kraeml.it>
2022-12-23 11:17:02 +01:00
Jonas Platte c215ac1f4b ci: Specify which version of wasm-pack we want 2022-12-22 16:06:48 +01:00
Jonas Platte 187696aa62 ci: Use latest version of wasm-pack-action 2022-12-22 16:06:48 +01:00
Jonas Platte e1e094f2f4 chore(sdk): Lower log level of event ID duplication event 2022-12-22 14:45:41 +01:00
Damir Jelić e2d4be2050 chore: Fix our build badge 2022-12-22 11:26:29 +01:00
Jonas Platte 7dc4d47d3c refactor(sdk): Store timeline event handler handles in a Vec
… instead of individual EventHandlerDropGuard's that each individually
hold a copy of Client.
2022-12-22 10:32:03 +01:00
Jonas Platte 9bad6faedd feat(sdk): Retry event decryption on forwarded room keys 2022-12-22 08:37:17 +01:00
Jonas Platte 3a715ea822 refactor(sdk): Move timeline m.room_key handler into new module 2022-12-22 08:37:17 +01:00
Jonas Platte f58f3fe055 chore(sdk): Add more logging to decryption retrying 2022-12-22 08:37:17 +01:00
Benjamin Kampmann 6eda742fff style(sliding-sync): explain why we limit the timeline items in freeze 2022-12-21 12:51:12 +00:00
Jonas Platte 14f451c6dd feat(bindings): Add decryption retrying to matrix-sdk-ffi 2022-12-21 13:35:27 +01:00
Benjamin Kampmann 562087a70e fix(sliding-sync): properly handle rooms_count == 1. fixes #1285 2022-12-21 10:13:39 +00:00
Benjamin Kampmann e3e0eb9144 fix(sliding-sync): sync view state for selective views.
Fixes ##1284
2022-12-21 10:13:39 +00:00
Benjamin Kampmann b6d48cb31a fix(sliding-sync): limit cold storage of timeline items to 10 and drop prev-batch token in case we'd store too many 2022-12-21 10:13:39 +00:00
Ivan Enderlin e4257f9aff feat(crypto-js): Make our promises reject with Error.
feat(crypto-js): Make our promises reject with `Error`.
2022-12-21 09:49:30 +01:00
Richard van der Hoff 6b871b0a48 matrix-sdk-crypto-js: Make our promises reject with Error
It's not a hard-and-fast-rule that Javascript exceptions should be `Error`
instances, but it's certainly a convention, and one that is going to reduce
surprises if we follow.
2022-12-20 22:36:42 +00:00
Jonas Platte 561fb97a7b feat(bindings): Add virtual timeline items to matrix-sdk-ffi 2022-12-19 13:54:55 +01:00
Jonas Platte a5080b6ed9 chore: Upgrade Ruma 2022-12-19 13:50:02 +01:00
Jonas Platte 8034ac20f9 refactor(base): Keep redaction events as Raw inside StateChanges 2022-12-19 13:50:02 +01:00
Jonas Platte f5c0ea4605 doc(bindings): Update docs for Apple platforms 2022-12-19 13:44:00 +01:00
Doug ddf448aa02 doc(bindings): Update docs for Apple platforms.
Remove old bash scripts now we have the xtask.
2022-12-19 12:31:29 +00:00
Kévin Commaille b960c372ac feat(sdk): Add support for stickers in the timeline 2022-12-19 08:39:07 +00:00
Jonas Platte 05e4bc0557 refactor: Don't reserialize member events before storing them 2022-12-15 18:19:35 +01:00
Jonathan de Jong d79c70177b Lab: The Setup Pattern 2022-12-15 16:48:55 +01:00
Kateřina Churanová db7eafb7d0 fix(doc): Fix unresolved link in documentation
Signed-off-by: Kateřina Churanová <k.churanova@famedly.com>
2022-12-14 22:00:25 +01:00
Anderas 9239470c1c feat(crypto): Add signalling to the verification requests and qr code verification 2022-12-14 19:15:22 +01:00
Benjamin Kampmann dab20638f9 fix(sliding-sync): new limit should default to None in builder 2022-12-14 14:06:31 +01:00
Benjamin Kampmann 273d0a0edf feat(sliding-sync): Growing window and limit count full sync (#1270)
Add a second full-sync-up mode to sliding sync. Previously - and still the default for backwards compatibility, but now named `PagingFullSync` - was to page through the list by the page-size, de-validating the previous page of items. The newly added `GrowingFullSync` instead grows the window by the given number `batch_size` per request, starting and keeping it from `0`. In the latter we might be pushing more data over the connection and are slightly slower, but the top always stays active and thus reactive to changes.

Furthermore the developer can now configure an optional maximum number to grow/paginate the full-sync up to (so stopping before actually having reached `count` whatever is smaller). This is already exposed via FFI, too.

- [x] add new full-sync-mode
- [x] add limited sync-up mode (similar to full sync but only to a limit `n`)
- [x] implement sync-up in jack-in
2022-12-14 13:50:35 +01:00
Jonas Platte 1c12b23e4c refactor(sdk): Move PaginationOutcome out of event_item module 2022-12-14 12:38:48 +01:00
Kévin Commaille 37bea19ab5 feat(sdk): Add day dividers to the experimental timeline 2022-12-14 12:29:24 +01:00
Jonas Platte eb0c3449fa refactor(bindings): Use proc-macro frontend more in sdk-ffi 2022-12-14 12:03:13 +01:00
Jonas Platte b3f146c932 refactor(bindings): Use proc-macro frontend more in crypto-ffi 2022-12-14 12:03:13 +01:00
Jonas Platte 34d458d3a3 chore: Upgrade UniFFI 2022-12-14 12:03:13 +01:00
Benjamin Kampmann 1c55565403 Merge pull request #1255 from gnunicorn/ben-update-jack-in
Sliding Sync updates

- expose timeline listener via FFI on slidingsyncroom, too - returning a stoppable spawn
- connect room timeline and sliding-sync-room timeline together
- increase HTTP timeout on jack-in
- add jack-in support for actual timeline items.
2022-12-13 15:14:57 +00:00
Richard van der Hoff c8da05125e Merge remote-tracking branch 'origin/main' 2022-12-12 18:41:30 +00:00
Richard van der Hoff 9ec9a59509 Release 0.1.0-alpha.2
Release `crypto-js` / Publish 🕸 [m]-crypto-js (push) Failing after 34s
2022-12-12 18:02:21 +00:00
Richard van der Hoff 4f9452c9b0 Fix yet another typo in package.json 2022-12-12 17:51:10 +00:00
Richard van der Hoff dfed2f1b3a Bump version
Release `crypto-js` / Publish 🕸 [m]-crypto-js (push) Failing after 36s
2022-12-12 17:35:54 +00:00
Richard van der Hoff 5850ff4cfc Fix filenames in package.json 2022-12-12 17:31:54 +00:00
Doug e64a8113e9 feat(bindings): Generate a Package.swift in xtask 2022-12-12 18:04:15 +01:00
Richard van der Hoff 2a4b83b5e6 Switch to npm-publish GHA script
Release `crypto-js` / Publish 🕸 [m]-crypto-js (push) Failing after 57s
Mostly because, as of #1167, we no longer have a `publish` script.
2022-12-12 16:17:32 +00:00
Benjamin Kampmann 799447f481 Merge remote-tracking branch 'origin/main' into ben-update-jack-in 2022-12-12 15:15:14 +01:00
Benjamin Kampmann 1a0b90291c fix(jack-in): rendering formatting improvements 2022-12-12 15:14:18 +01:00
Valere 9a058b9ea0 feat(crypto): Request room keys if the decryption failure is an unknown message index
We automatically request room keys to be forwarded from our other trusted devices if a decryption failure happens because the room key is missing.

This patch introduces automatic room key requests for decryption failures if the room key is available but has been ratcheted forward. In other words, we will now request a better version of the given room key automatically as well.

Co-authored-by: Damir Jelić <poljar@termina.org.uk>
2022-12-12 13:09:45 +01:00
Erwan Bousse 1ef645d285 feat(bindings): Use HTTPS proxy when provided as environment variable 2022-12-09 16:01:02 +01:00
valere 57e09c07dc Git Ignore generated files from kotlin bindings 2022-12-09 13:12:50 +01:00
Benjamin Kampmann 1e4c16b9a9 Merge remote-tracking branch 'origin/main' into ben-update-jack-in 2022-12-07 16:31:32 +01:00
Benjamin Kampmann 26486fc905 feat: add kotlin bindings
Merge pull request #1239 from matrix-org/ganfra/kotlin_bindings
2022-12-07 14:50:38 +00:00
Benjamin Kampmann c4884887ef fix(sliding-sync): set views to preload if we've recovered from frozen 2022-12-07 13:59:50 +00:00
Jonas Platte ddde87f14b fix(sdk): Fix remote echo event_id check
The send-event response sets the event_id field, not the timeline key.
The previous error branch wasn't actually reachable.
2022-12-07 12:32:44 +01:00
Jonas Platte 3e1fddccfd fix(sdk): De-duplicate local echoes with remote echoes without txn ID 2022-12-07 12:32:44 +01:00
Jonas Platte c1949e3fc6 refactor(sdk): Move timeline::add_event_id to TimelineInner 2022-12-07 12:32:44 +01:00
Juliette 63bc004e26 feat(bindings): Add uploading media and setting display name to FFI
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2022-12-07 10:40:15 +00:00
Jonas Platte e99341df69 chore(bindings): Move bindings around to work around rustfmt bug 2022-12-07 11:16:25 +01:00
Jonas Platte 7667f0c429 refactor(sled): Use open_with_database for SledCryptoStore::open 2022-12-07 11:16:25 +01:00
Jonas Platte d0609822bc refactor(sled)!: Rename SledCryptoStore::{open_with_passphrase => open}
`open_with_db` also has the passphrase parameter without mentioning it
in its name. The old name also sounded like the passphrase was required
when it's actually optional.
2022-12-07 11:16:25 +01:00
Jonas Platte 262fe5630f feat(sdk): Implement IntoFuture for LoginBuilder and SsoLoginBuilder 2022-12-07 11:13:44 +01:00
Andrew Ferrazzutti 2176b7ee39 Revise example of machine.receiveSyncChanges
Clarify that the JSON-encoded `toDeviceEvents` passed to
`OlmMachine.receiveSyncChanges` must be the list of events themselves,
instead of a wrapper object that contains the event list.

Signed-off-by: Andrew Ferrazzutti <andrewf@element.io>
2022-12-07 11:00:22 +01:00
Benjamin Kampmann 2bc738be8c feat(jack-in): sending messages via timeline API 2022-12-06 14:28:34 +01:00
Jonas Platte f5b59c3de2 refactor(sdk)!: Remove impl Into<_> around Ruma request types
The SDK used to have builder types for these request types, but they
were removed long ago.
2022-12-06 14:00:33 +01:00
ismailgulek 31e5d4d663 feat(bindings): Add members accessor to Room
Co-authored-by: Doug <douglase@element.io>
2022-12-06 12:58:30 +00:00
Jonas Platte 6a386ca5fb feat(bindings): Add sending of reactions to matrix-sdk-ffi 2022-12-06 12:48:06 +01:00
Jonas Platte a18919bf50 chore: Add a PR template 2022-12-05 14:58:33 +01:00
Jonas Platte f92153757b chore: Add CONTRIBUTING.md
… and move the testing section from README.md into it.
2022-12-05 14:58:33 +01:00
Benjamin Kampmann 83d0abc6d7 feat(sliding-sync): properly handle all timeline event types 2022-12-05 14:52:38 +01:00
Benjamin Kampmann f919ee3b7d fix(ffi): clean up sliding sync timeline API 2022-12-05 14:52:38 +01:00
Benjamin Kampmann 0bfa63b31e fix(sliding-sync): return a stoppable spawn to allow listener cancellation 2022-12-05 14:52:38 +01:00
Benjamin Kampmann 229e6b28a5 fix(ffi): connect timelines together 2022-12-05 14:52:38 +01:00
Benjamin Kampmann fee9d52dde feat(ffi): expose sliding-sync timeline 2022-12-05 14:52:38 +01:00
Benjamin Kampmann d4890807f9 fix(jack-in): bump http timeout to 90seconds 2022-12-05 14:52:38 +01:00
Damir Jelić 58f92e59fe fix(bindings): Change the is_syncing method to read the correct value 2022-12-05 14:16:01 +01:00
Damir Jelić 1f38becdd9 chore: Fix a clippy warning 2022-12-05 14:16:01 +01:00
Ivan Enderlin 2d9e8170fe feat(indexeddb): Update to indexed_db_futures 0.3.0.
It removes the fork we have introduced in https://github.com/matrix-org/matrix-rust-sdk/pull/1068
(all our patches have been merged and are part of this 0.3.0 release).
2022-12-05 14:16:01 +01:00
Kévin Commaille aeb419207a refactor(sdk): Split restore_session into two parts at the Client level (#1246)
Tokens are not necessary for the restoration of the crypto/store, only
the meta. Required for OpenID Connect support where we need the tokens to get the
meta.
2022-12-05 13:19:35 +01:00
Flix ee713d42ae chore: Bump axum 2022-12-01 13:26:07 +01:00
ganfra 97578e418c Merge branch 'main' into ganfra/kotlin_bindings 2022-11-30 20:39:42 +01:00
Benjamin Kampmann 7d69fb2314 fix(sliding-sync): fallback to invite-room if regular room can't be found (#1249)
Co-authored-by: Jonas Platte <jplatte+git@posteo.de>
2022-11-30 13:59:25 +00:00
Jonas Platte 32cc477f54 refactor(sdk): Remove media caching from in-memory state store 2022-11-30 10:21:24 +01:00
Andy Uhnak 457c5f4fa3 Add cancel methods and rename callback method 2022-11-30 10:18:47 +01:00
Damir Jelić c604e59dcc feat(bindings): Expose the Sas::changes method over the FFI 2022-11-30 10:18:47 +01:00
Damir Jelić 026659ef68 refactor!(bindings): Return objects for the verification support
When the verification support was initially bound, Uniffi did not
support objects (the OlmMachine) returning other objects (our
verification objects). Instead we converted all the verification objects
into pure data structs which the other side had to poll for changes.

Now that Uniffi supports returning objects we can refactor this and make
the API on the other side the same as on the Rust side.
2022-11-30 10:18:47 +01:00
Jonas Platte 195ade911e refactor(sdk): Simplify signature of handle_back_paginated_event 2022-11-29 23:55:33 +01:00
Jonas Platte a881dad529 refactor(sdk): Make Timeline construction non-async 2022-11-29 23:55:33 +01:00
Jonas Platte 090b4495ea refactor(sdk): Simplify signature of handle_remote_event 2022-11-29 23:55:33 +01:00
Jonas Platte 3dfc60506f feat(sdk): Add SlidingSyncRoom::latest_event 2022-11-29 23:55:33 +01:00
Jonas Platte d4b67de9c3 refactor: Replace Timeline::latest with Timeline::latest_event 2022-11-29 23:55:33 +01:00
Jonas Platte 78e621010d refactor(sdk): Make tracking of fully-read marker in Timeline optional 2022-11-29 23:55:33 +01:00
Jonas Platte 1b0849a20c refactor(sdk): Move TimelineInner into its own module 2022-11-29 23:55:33 +01:00
Jonas Platte 32bf3143c7 doc: Hint towards test execution 2022-11-29 23:38:31 +01:00
Jonas Platte 3d22b6d5a4 chore: Upgrade Ruma 2022-11-29 15:37:18 +01:00
Flix bc29bcef38 doc: Hint towards test execution 2022-11-29 15:29:41 +01:00
Jonas Platte 859b12fca9 feat(sdk): Add debug logs for aggregated event handler execution times 2022-11-29 09:46:43 +01:00
Jonas Platte c564696a8a feat(sdk): Add tracing info to call_event_handlers 2022-11-29 09:46:43 +01:00
Benjamin Kampmann d3ff402813 feat(sliding-sync): Offline caching for Sliding Sync & session recovery (#1193)
With these changes, the user of sliding sync can configure the SlidingSync-Builder to store and recover the state from storage. For that they should call cold_cache("my-sliding-sync-key") with their preferred storage location during setup time on SlidingSyncBuilder. If a cached version is found under that key, the internal state of the rooms-list as well as each view found (identified by their name) will be set from storage immediately - allowing the user to show the last cached state. The Builder then also uses the same key to store its latest state after every update received from remote.

👉 Note on room list:
As we store a disconnected state, we are saving all RoomList entries as either Empty or Invalidated. This allows for an easier updating when the server comes back with results, as we don't track the ranges in cache - in the view of the server, we haven't seen anything yet.

👉 Note on timeline events:
This does store all existing timeline events - initial and seen during the session (as we keep track of them right now) - and will recover that state. However, as we can't be sure wether we have gaps in the timeline, the timeline items are reset upon receiving updates for them from the server.

👉 Note on (full-sync-)view:
While we are caching the server returned results (view list, room count and room info) as is, we are not caching the internal state (whether we are catching up) nor the ranges (as they are likely to be out of date) - those will be acting the same way you configured them, just with preloaded results. So for a full-sync-view, it will still do requests in batches and replace the corresponding set of rooms. Which could mean you might see the same room appear twice, though the cached one would be marked as invalidated. This might be problematic if the list the UI shows is longer than the batches fetched, but would be resolved quickly when catching up.

👉 On recovery:
When the sliding sync receives a M_UNKNOWN_POS, indicating the server has expired our session, sliding sync now transparently retries with up to three times to restart the sliding sync with full set of extensions and the latest views at their existing windows, the current room state is held. For full sync this starts a new sync-up with the existing room list staying intact. This also works from the offline at start.
2022-11-28 18:01:04 +00:00
Kateřina Churanová 08951e1c56 fix(ci): Fix unused import warnings on macos 2022-11-28 15:08:01 +01:00
Jonas Platte afda63f9e2 fix(sdk): Only retry requests on M_LIMIT_EXCEEDED or HTTP 5xx 2022-11-28 14:13:03 +01:00
Jonas Platte d7e6dd22e6 refactor(sdk): Simplify RumaApiError again
by folding Uiaa(UiaaResponse::MatrixError) into RumaApiError::ClientApi.
2022-11-28 14:13:03 +01:00
ganfra 68a6d2707f Fix naming of generated crypto .so files 2022-11-28 11:47:59 +01:00
ganfra 6666cd7a79 Use the generate .a file from cargo build as it's not stripped (instead of new target) 2022-11-25 19:20:54 +01:00
ganfra 75bb44bebf Add script to generate only crypto-ffi crate 2022-11-25 11:59:27 +01:00
ganfra 0504240878 Merge branch 'main' into ganfra/kotlin_bindings 2022-11-25 10:19:19 +01:00
Jonas Platte c0910a3693 chore: Fix a typo, upgrade typo check action 2022-11-24 22:36:46 +01:00
Kévin Commaille 183d4595aa fix(sdk): Make sure read marker can't go backwards 2022-11-24 22:15:36 +01:00
Kévin Commaille 580861cd1c fix(sdk): Rename read marker functions and variables for clarity 2022-11-24 22:15:36 +01:00
Kévin Commaille 1f761f3c13 test(sdk): Add read marker case when fully read event wasn't found 2022-11-24 18:17:43 +01:00
Kévin Commaille 5dc6416a76 fix(sdk): Fix logic if read marker event was not in the timeline 2022-11-24 18:17:43 +01:00
Jonas Platte c808a72914 feat(sdk): Add FailedToParse timeline items 2022-11-24 14:35:49 +01:00
Jonas Platte 6a1edf133d refactor(sdk): Add NewEventTimelineItem::from_content 2022-11-24 14:35:49 +01:00
Jonas Platte d0f0b650bd chore: Move impl for type after its definition 2022-11-24 14:35:49 +01:00
Jonas Platte d027005e87 chore(sdk): Rewrap info! invocation 2022-11-24 14:35:49 +01:00
Jonas Platte 43f0ba7711 chore: Appease clippy 2022-11-24 13:43:09 +01:00
Jonas Platte 8ce216974f test(sdk): Add a regression test for read marker updates 2022-11-24 13:43:09 +01:00
Jonas Platte f7d4129607 fix(sdk): Fix logic for updating an existing read marker 2022-11-24 13:43:09 +01:00
Stefan Ceriu 1107f27c3d fix(ffi): Use the right path for generated source files and only copy the generated folder contents (#1226) 2022-11-22 14:21:58 +00:00
Damir Jelić 3113f6698f chore: Don't use the term check for signature verification 2022-11-22 13:21:33 +01:00
Damir Jelić 38c38bc9f0 feat!(bindings): Expose the improved result of the verify_backup method 2022-11-22 13:21:33 +01:00
Jonas Platte de71d7e434 refactor(base): Simplify default memory store initialization 2022-11-22 11:03:45 +01:00
Jonas Platte 57a1743566 chore: Update Cargo.lock 2022-11-22 11:01:23 +01:00
Stefan Ceriu 1025d42624 fix(ffi): Fix xcframework release script, add missing module map 2022-11-22 09:55:46 +01:00
Jonas Platte fcb37b6962 feat: Support creating Timeline with initial items from sliding sync
Co-authored-by: Benjamin Kampmann <ben@gnunicorn.org>
2022-11-21 14:52:02 +01:00
bitfriend 2ab697328f fix(sdk): Make handle_back_paginated_event future Send 2022-11-18 20:57:36 +00:00
Damir Jelić eb20abe7b8 chore: The encryption feature was renamed 2022-11-18 19:53:07 +01:00
Jonas Platte fa71122e7d ci: Add sliding-sync and experimental-timeline to clippy check 2022-11-18 15:05:48 +01:00
Jonas Platte 785a3349ab refactor(sdk)!: Make sync_token accessor private 2022-11-18 14:02:41 +01:00
Jonas Platte 6329dbe6c2 refactor!: Split SyncResponse into two types
- matrix_sdk_base::sync::SyncResponse is the internal representation that
  we can update to account for sliding sync
- matrix_sdk::sync::SyncResponse is `/v3/`-specific and should not change
2022-11-18 14:02:41 +01:00
Jonas Platte ec5306978e refactor(sdk): Simplify signature of handle_sync_response 2022-11-18 14:02:41 +01:00
Jonas Platte 6b363120ef fix(sdk): Fix errors & warnings w/ experimental-timeline, w/o e2e-encryption 2022-11-18 13:39:51 +01:00
Jonas Platte 4bafb3818b feat(sdk): Add Timeline::retry_decryption 2022-11-18 13:39:51 +01:00
Jonas Platte 317965995a test(sdk): Add a test for retrying decryption of timeline items 2022-11-18 13:39:51 +01:00
Jonas Platte 74e9209e95 feat(sdk): Retry decryption of UTD timeline items 2022-11-18 13:39:51 +01:00
Jonas Platte da76a2700d refactor(sdk): Use an async lock for TimelineInnerMetadata 2022-11-18 13:39:51 +01:00
Jonas Platte ac33ca5fa0 refactor(sdk): Hold locks for the full lifetime of TimelineEventHandler
Not really necessary now, but required for thread-safe bulk updates like
we want for retrying decryption.
2022-11-18 13:39:51 +01:00
Jonas Platte 2bc0ac8fd7 refactor(sdk): Merge mutexes in TimelineInner into one 2022-11-18 13:39:51 +01:00
Damir Jelić 5a108f53cc refactor(crypto): Only use the Mutable for the inner SAS object
This patch makes the SAS signalling more robust, it ensures that we
can't forget to signal changes to the state.
2022-11-18 09:26:20 +01:00
Damir Jelić 1f2cdfc601 chore(contrib): Mitmproxy 9 has removed the HTTP prefix from the Response type 2022-11-17 12:43:56 +01:00
Jonas Platte 02165f7a05 refactor(sdk)!: Move retrying out of HttpSend trait 2022-11-17 12:32:45 +01:00
Benjamin Kampmann 98600e4c1e feat(jack-in): sending messages (#1156) 2022-11-17 11:18:27 +00:00
Jonas Platte bb6145d581 refactor!: Move large parts of deserialized_responses to matrix-sdk-base 2022-11-17 11:33:37 +01:00
Jonas Platte 975626a4f8 refactor(common)!: Remove unused TimelineSlice type 2022-11-17 11:33:37 +01:00
Jonas Platte c3aa03e486 chore: Add reldbg profile and use it for matrix-sdk-ffi iOS builds 2022-11-16 09:59:06 +01:00
Jonas Platte 51798e90bd chore: Add dbg profile and improve profile comments 2022-11-16 09:59:06 +01:00
Jonas Platte 5fa6f34e41 refactor(sdk): Use ErrorKind for get_state_events_for_key response check 2022-11-15 16:06:21 +01:00
Jonas Platte f9d2d32337 fix(xtask): Fix clippy warning 2022-11-15 16:06:21 +01:00
Jonas Platte 2dd2763d39 refactor(base): Unify identical branches and erase empty branches 2022-11-15 16:06:21 +01:00
Johannes Becker bb6cf83a39 refactor(appservice)!: Rename virtual user to appservice user 2022-11-15 15:57:31 +01:00
Damir Jelić a7dd690189 refactor(crypto): Split out the room key forwarding logic into a separate method 2022-11-15 14:57:46 +01:00
Jonas Platte e060606331 refactor(crypto): Move some code into a new method 2022-11-15 14:57:46 +01:00
Jonas Platte a30e40ed3a refactor: Introduce more early returns to reduce rightwards drift 2022-11-15 14:57:46 +01:00
Jonas Platte e59acfe28c refactor: Use let-else to remove boilerplate code 2022-11-15 14:57:46 +01:00
Jonas Platte d312aaecec refactor(crypto): Replace ? on if-else by early return 2022-11-15 14:57:46 +01:00
Jonas Platte 2528a5501b refactor(crypto): Remove unnecessary ref mut in patterns 2022-11-15 14:57:46 +01:00
Jonas Platte cf241d8c32 refactor(crypto): Merge matches and reflow comments in accept_secret 2022-11-15 14:57:46 +01:00
Jonas Platte db5d34e385 refactor(crypto): Reduce rightwards drift in test_device_signatures 2022-11-15 14:57:46 +01:00
Jonas Platte 7d2865f004 refactor(bindings): Shorten is_transaction_id_valid method 2022-11-15 14:57:46 +01:00
Jonas Platte 826705c174 chore: Bump MSRV to 1.65 2022-11-15 14:57:46 +01:00
Jonas Platte 5a1e347333 ci: Install stable toolchain for test-uniffi-codegen
The xtask is explicitly using the stable, and there is no reason to use
a less stable toolchain for this job.
2022-11-15 14:57:46 +01:00
Benjamin Kampmann 882b206144 feat(xtask): build xcframework
* Move swift build scripts into xtask (#1201)
* fix(ffi): use target_path from `cargo metadata` rather than guessing
* ci(ffi): install necessary target arch for build-framework test
* feat(xtask): copy to target without rsync.
2022-11-15 13:06:33 +01:00
Flix 8d89037296 fix: Missing ServerError after ruma update 2022-11-15 12:32:20 +01:00
Flix deb8ef801b fix: Remove the condition to only set new members in get_members 2022-11-15 12:32:20 +01:00
Flix 0a45e401e3 test: Use MemoryStore to fix problems in coverage tests 2022-11-15 12:32:20 +01:00
Flix 456e00e953 fix: Add missing not_found errors 2022-11-15 12:32:20 +01:00
Flix ab846f79f1 test: Add encryption state mock to tests where it is needed 2022-11-15 12:32:20 +01:00
Flix b1c2da9a68 fix: Don't mark encryption state to be synced on sync
As it appears, the first sync for a room might not include the
encryption state, so we cannot set the encryption state to be synced on
incoming syncs. That way, we always fetch the encryption state manually.
2022-11-15 12:32:20 +01:00
Flix 1ab33a28c9 fix: Make sure room.sync_up works under all conditions 2022-11-15 12:32:20 +01:00
Flix a3a9858bf4 refactor: Make if branches instead of early return 2022-11-15 12:32:20 +01:00
Flix 6951f7f5bf fix: Review comments 2022-11-15 12:32:20 +01:00
Flix fb8045b254 fix: Fix UDL or is_encrypted 2022-11-15 12:32:20 +01:00
Flix be7c3239a8 refactor: Adjust create_dm_room to new room API 2022-11-15 12:32:20 +01:00
Flix 956e270941 feat: Implement room sync_up method 2022-11-15 12:32:20 +01:00
Flix 65721aafb9 fix: Lock syncing to the store to avoid races 2022-11-15 12:32:20 +01:00
Flix 9d150e5cc6 fix: Adjust repeated_join test to new changes 2022-11-15 12:32:20 +01:00
Flix 43dd4452cd fix: Check encryption state in intermediate rooms 2022-11-15 12:32:20 +01:00
Flix 4abb08c4a1 feat!: Make intermediate rooms available right after joining/leaving
Co-Authored-By: Jonas Platte <jplatte@matrix.org>
Co-Authored-By: Benjamin Kampmann <ben@gnunicorn.org>
2022-11-15 12:32:20 +01:00
Jonas Platte 544da0d324 fix(sdk): Fix broken intra-doc link 2022-11-15 11:54:53 +01:00
ismailgulek 29aa9a78c3 feat(ffi): Expose file messages
Expose file messages (#1203)
2022-11-14 15:56:38 +00:00
Jonas Platte 4dc0e0ef3c chore: Upgrade Ruma 2022-11-14 15:01:53 +01:00
Gabriel Féron 680ef6b93a feat(sdk): Allow using an existing sled::Db with SledStateStore 2022-11-12 13:04:34 +00:00
Benjamin Kampmann 078855b4c9 feat(ffi): logging support for android (#1199)
* split logger into platform specific implementations
* logging support for android
* use platform independent wrapper for uniffi:exports
* activate colors for ansi-terminals
2022-11-11 12:20:19 +00:00
Damir Jelić 0d80c0e3fe chore(crypto): Improve the log for the one-time key signature error 2022-11-10 19:03:14 +01:00
ganfra 0901243440 Merge branch 'main' into ganfra/kotlin_bindings 2022-11-10 15:34:32 +01:00
ganfra fc76cc443e Fix build script 2022-11-10 15:34:00 +01:00
Jonas Platte 45cf32dfab test(sdk): Add a test for an undecryptable event 2022-11-10 13:43:46 +01:00
Jonas Platte aa4dbe4bc6 test(sdk): Add a test for editing a redacted event 2022-11-10 13:43:46 +01:00
Jonas Platte b550e9ba14 test(sdk): Add a test for an invalid edit 2022-11-10 13:43:46 +01:00
Jonas Platte 79db05cffb test(sdk): Add a unit test for redacting a reaction 2022-11-10 13:43:46 +01:00
Richard van der Hoff e45c57d8fe feat(crypto-js): allow async load of webassembly (#1198)
It turns out that Google Chrome refuses to initialise the wasm via the
synchronous `WebAssembly.Module` constructor, complaining that it is too
big. To be fair, it has a point.

Anyway, that means we need to provide a way to load the wasm
asynchronously. So, we introduce an `initAsync()` function which applications
can call before they do anything else, to load the wasm in the background.

If the app *doesn't* call `initAsync()`, then we load the wasm synchronously
the first time a function that accesses the wasm is called.
2022-11-10 11:51:37 +00:00
Ivan Enderlin 2eefb3a090 fix(indexeddb) + feat(crypto-js): Releasing IDBDatabase so that we can delete them
fix(indexeddb) + feat(crypto-js): Releasing `IDBDatabase` so that we can delete them
2022-11-10 10:40:16 +01:00
Ivan Enderlin 6754defb9b doc(crypto-js): Improve documentation of OlmMachine.close. 2022-11-10 10:21:48 +01:00
Damir Jelić 078a75ea27 chore(crypto): Log the protocols we accepted for a SAS verification 2022-11-09 16:56:23 +01:00
Damir Jelić 0d74189cde feat(crypto): Implement MSC3783
This commit adds support for the message authentication code calculation
that is using valid base64 when encoding the MAC.
2022-11-09 16:56:23 +01:00
Ivan Enderlin 196dfaea03 test(crypto-js): Test OlmMachine.close + IndexedDB clean up. 2022-11-09 14:48:37 +01:00
Ivan Enderlin f6496d01c7 feat(crypto-js): Add OlmMachine.close.
This new `OlmMachine.close` forces to drop/close the `OlmMachine` without
waiting on the JavaScript garbage collector to collect it.

`wasm-bindgen` generates the following JS glue code:

```js
close() {
    const ptr = this.__destroy_into_raw();
    wasm.olmachine_close(ptr);
}
```

And, `__destroy_into_raw` looks like this:

```js
__destroy_into_raw() {
    const ptr = this.ptr;
    this.ptr = 0;
    OlmMachineFinalization.unregister(this);
    return ptr;
}
```

It unregisters itself from the `FinalizationRegistry` correctly. We are
protected from a double-free.
2022-11-09 14:48:37 +01:00
Jonas Platte ca515997f1 feat(bindings): Expose video messages in matrix-sdk-ffi 2022-11-09 14:45:31 +01:00
Ivan Enderlin 3fe63c328f fix(indexeddb): Call IDBDatabase.close manually.
Surprisingly, `indexed_db_futures::IdbDatabase` is not closed when dropped.
Hopefully, there is a [`IdbDatabase::close(&self)`][close] method, which calls
`web_sys::IdbDatabase.close`, aka [`IDBDatabase.close`][websys-close], so let's
use it!

`IDBDatabase.close` returns immediately and closes the connection in a separate
thread. The connection isn't actually closed until all transactions created
using this connection are complete. No new transactions can be created for this
connection once this method is called. Methods that create transactions throw
an exception if a closing operation is pending.

[close]: https://github.com/Alorel/rust-indexed-db/blob/8c106eb418aecdba2f3fde80d91a4673a875fdf6/src/idb_database.rs#L73-L77
[websys-close]: https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/close
2022-11-09 14:45:00 +01:00
ismailgulek 26d690a21c Fix clippy 2022-11-09 16:34:41 +03:00
ismailgulek 045d6647b2 feat(bindings): Add support for editing messages 2022-11-09 14:15:19 +01:00
ismailgulek 7f54e4592f Expose video messages 2022-11-09 16:02:40 +03:00
ganfra 4899f98f7f Merge main into ganfra/kotlin_bindings 2022-11-08 17:25:57 +01:00
Kévin Commaille 93be96c85c feat(sdk): Add read marker logic to the timeline API 2022-11-08 13:16:09 +01:00
Jonas Platte c014f980cb chore(sdk): Remove invalid comment
I have tried for hours and have concluded it is probably not possible
even with GATs to have RawEvent borrow its inner value.
It is also not a commonly-used feature, so removing the unnecessary
clones is not that important.
2022-11-04 15:17:01 +01:00
Jonas Platte 5601435449 refactor(sdk): Simplify bounds on event handler registration methods
… by introducing a hidden trait that constrains the output type of the
associated type EventHandler::Future.
2022-11-04 15:17:01 +01:00
Jonas Platte 5b919fc9df refactor: Fix clippy lints 2022-11-04 15:17:01 +01:00
Stefan Ceriu d57666c0b8 fix(sliding_sync): remove server versions fetch
before being able to use the sliding sync builder; the versions are still fetched but at a later date (#1183)
2022-11-04 12:03:17 +00:00
Jonas Platte ce966ed6ce chore: Upgrade criterion, pprof 2022-11-04 11:14:13 +01:00
Jonas Platte 3a6d048b9f refactor(sdk)!: Rename uiaa_response methods to as_uiaa_response 2022-11-03 18:05:04 +01:00
Jonas Platte cf779822fe refactor(sdk): Merge impl blocks 2022-11-03 18:05:04 +01:00
Jonas Platte c8dd5c42e9 docs(sdk): Add more doc links to as_ruma_api_error methods 2022-11-03 18:05:04 +01:00
Jonas Platte 128c74ace5 refactor(sdk): Use server-supplied retry time when when available 2022-11-03 18:05:04 +01:00
Jonas Platte 85ea9554e5 feat(sdk): Add {RumaApiError,HttpError,Error}::as_client_api_error 2022-11-03 18:05:04 +01:00
Ivan Enderlin 5b25b8967c feat(crypto-js): Encode the WASM as base64 for portability
feat(crypto-js): Encode the WASM as base64 for portability
2022-11-03 16:44:16 +01:00
Damir Jelić 3b755fc15e chore(crypto): Fix some spelling
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2022-11-03 16:18:15 +01:00
Damir Jelić da61005ee4 chore(crypto): Improve the logs for the SAS state transitions 2022-11-03 16:18:15 +01:00
Damir Jelić 7a263433c7 refactor!(crypto): Add a new SAS state that waits for a key to be sent
This patch adds a couple more states to the SAS verification state
machine. Namely we add the following states:

* KeySent
* KeysExchanged

This prevents users from confirming that the short auth string matches
before they have sent out a m.key.verification.key event, which is
necessary for the other side to present the short auth string.

The KeysExchanged state functionally replaces the KeyReceived state.
Meaning that the short auth string can only presented once we
transitioned into the KeysExchanged state.

We can transition into the KeysExchanged state from the KeyReceived
state or from the KeySent state, depending on if we first receive a
m.key.verification.key event or if we first send out our own
m.key.verification.key event.

This means that, if the transition into the KeysExchanged state happens
through the KeySent state, users won't be able to tell if the transition
happened. In other words, listening to `m.key.verification` events
doesn't work anymore, users will need to use the new `Sas::changes()`
API to listen to a stream of state changes.
2022-11-03 16:18:15 +01:00
Ivan Enderlin ee27c19bf7 chore: Removing trailing spaces. 2022-11-03 15:57:20 +01:00
Ivan Enderlin 6d21df6d29 !debug off 2022-11-03 15:37:54 +01:00
Ivan Enderlin e989bc2377 fix(crypto-js): scan_qr_code takes a reference to QrCodeScan. 2022-11-03 15:37:21 +01:00
Jonas Platte 28b44a7f03 refactor!: Rename restore_login to restore_session 2022-11-03 15:34:31 +01:00
Ivan Enderlin 265ac1f97b chore(crypto-js): Configure a profiling profile for wasm-pack. 2022-11-03 14:59:22 +01:00
Ivan Enderlin d044565caa test(crypto-js): Keep qr.toBytes() local, to avoid weird GC collection. 2022-11-03 14:57:07 +01:00
Ivan Enderlin aa7d225867 feat(crypto-js): Qr.to_bytes returns a Uint8ClampedArray. 2022-11-03 13:58:17 +01:00
Stefan Ceriu af2de2d8ef chore(ffi): Reduce the log level for some not so useful logs 2022-11-03 13:26:32 +01:00
Jonas Platte a28a664302 refactor(bindings)!: Replace RestoreToken string with Session dictionary 2022-11-03 12:58:34 +01:00
Jonas Platte c1423e9326 refactor(bindings)!: Remove is_guest
It's not used by anything and should be re-introduced in the main Rust
first if it's needed.
2022-11-03 12:58:34 +01:00
Jonas Platte 8bfc186595 feat(bindings): Add TimelineItemContent::as_unable_to_decrypt 2022-11-03 12:03:33 +01:00
Jonas Platte 0cf5356f15 feat(sdk): Add UnableToDecrypt as a variant of TimelineItemContent 2022-11-03 12:03:33 +01:00
Jonas Platte c03940e6d5 chore(sdk): Reword log messages 2022-11-03 12:03:33 +01:00
Jonas Platte f93170323b refactor(sdk): Remove unnecessary pub(crate) 2022-11-03 12:03:33 +01:00
Ivan Enderlin a3cdd31713 fix(crypto-nodejs): Pass secrets to the release workflow
fix(crypto-nodejs): Pass secrets to the release workflow
2022-11-03 11:58:59 +01:00
Ivan Enderlin fb89de8267 fix(crypto-nodejs): Pass secrets to the release workflow.
See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsecrets.
2022-11-03 11:30:10 +01:00
Ivan Enderlin 223e65fc26 !debug 2022-11-03 11:08:52 +01:00
Jonas Platte b5b2eafbec refactor(base): Remove unused Error variants 2022-11-03 10:52:11 +01:00
Jonas Platte 27b0ea1aa0 refactor(crypto): Move EncryptionNotEnabled out of MegolmError
… into matrix_sdk_base::Error because it is never constructed in the
matrix_sdk_crypto crate that defines MegolmError.
2022-11-03 10:52:11 +01:00
Jonas Platte 4c4b1f3abc refactor(bindings): Use uniffi proc-macros more in sdk-ffi 2022-11-03 09:48:41 +01:00
Jonas Platte 99e621a82b chore: Upgrade UniFFI 2022-11-03 09:48:41 +01:00
ganfra 4eedbfbc11 Merge branch 'main' into ganfra/kotlin_bindings 2022-11-02 19:06:07 +01:00
Richard van der Hoff 898265b257 Clean up build script 2022-11-02 17:22:00 +00:00
Richard van der Hoff 9d400a7494 fix JS syntax 2022-11-02 17:13:58 +00:00
Ivan Enderlin a4ca6dbf38 !debug 2022-11-02 17:25:13 +01:00
Ivan Enderlin b23d30c951 fix(indexeddb): Fix wasm_bindgen::JsValue::(from|to)_serde warnings
fix(indexeddb): Fix `wasm_bindgen::JsValue::(from|to)_serde` warnings
2022-11-02 16:58:09 +01:00
Ivan Enderlin bb96ab89f8 fix(crypto-js): Make build.sh cross-platform-ish. 2022-11-02 16:57:26 +01:00
Ivan Enderlin 4cd8a8400f chore: Reorder dep names. 2022-11-02 16:38:01 +01:00
Ivan Enderlin 28d4a69552 !fixup 2022-11-02 16:32:20 +01:00
Jonas Platte a6ca114bac chore: Upgrade UniFFI 2022-11-02 16:21:16 +01:00
Ivan Enderlin 6497d6d676 feat(crypto-js): Make scripts/build.sh compatible with macOS.
This patch also improves the “phrasing”, simplifies the code a little bit etc.
Small stuff.
2022-11-02 16:19:17 +01:00
Ivan Enderlin da93cf7dc4 fix(indexeddb): Fix wasm_bindgen::JsValue::(from|to)_serde warnings.
`wasm_bindgen::JsValue::(from|to)_serde` now emit warnings because of a
circular dependency. To solve this problem, this patch now uses `gloo-utils`,
see https://rustwasm.github.io/wasm-bindgen/reference/arbitrary-data-with-serde.html#an-alternative-approach---using-json.

Ideally, we would like to use `serde_wasm_bindgen` but the behaviour isn't
the same. `gloo-utils` serializes and deserialized through JSON, whilst
`serde_wasm_bindgen` manipulates the `JsValue` directly which can be better or
worst dependending of the case. This patch conserves the JSON approach as it
was the previous and tested one.
2022-11-02 08:59:37 +01:00
Ivan Enderlin 2b4f4b17c3 chore: Update wasm-bindgen's ecosystem to latest versions. 2022-11-02 08:59:21 +01:00
Richard van der Hoff 0f104c7433 Fix unbase64 loading 2022-11-01 13:47:44 +00:00
Jonas Platte 726e4b9aa0 fix(appservice): Fix nesting of AppServiceRouter in axum::Router 2022-11-01 11:37:16 +01:00
Jonas Platte f227f583e0 chore: Upgrade ruma-client-api 2022-11-01 11:34:05 +01:00
Jonas Platte 36444cd3a0 chore: Clean up TOML formatting 2022-11-01 11:34:05 +01:00
Jonas Platte 9c489b398d chore: Upgrade clap dependency of xtask 2022-11-01 11:34:05 +01:00
Richard van der Hoff ce03f016b9 Copy unbase64.js into the right place 2022-11-01 10:19:13 +00:00
Richard van der Hoff 4557494da6 Optimise unbase64
Use a lookup table instead of a function with if statements
2022-11-01 10:18:44 +00:00
Richard van der Hoff 093856671a Encode the WASM as base64
Some nasty hackery to get around the nastiness of the JS ecosystem
2022-10-31 22:39:44 +00:00
Ivan Enderlin 471ac07c88 fix(crypto-nodejs): Set KeepAlive to false since Node.js v19
Disable keepAlive in download-lib.js, allowing node bindings to work on Node.JS 19
2022-10-31 14:35:57 +01:00
Will Hunt f4f3ca4b25 A newline 2022-10-31 13:18:16 +00:00
Will Hunt c009f54ba9 Tidy 2022-10-31 13:17:48 +00:00
Will Hunt 546dc9a652 Run CI against 19 2022-10-31 13:04:05 +00:00
Will Hunt 717f86332f Disable keepalive to prevent hanging connections 2022-10-31 12:39:23 +00:00
Jonas Platte 6c975b01b5 Revert #1151
This reverts the commits

- cacb20e3ef
- 01cc896dab
2022-10-31 12:03:23 +01:00
Ivan Enderlin 182733e984 feat(crypto-js): OlmMachine.initialize is the new constructor
feat(crypto-js): `OlmMachine.initialize` is the new constructor
2022-10-31 11:41:50 +01:00
Andy Uhnak e4964b92a2 Local trust 2022-10-31 11:14:04 +01:00
Ivan Enderlin bdf460cd91 doc(crypto-js): Rephase something.
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2022-10-31 11:07:21 +01:00
Ivan Enderlin 0276552c72 feat(crypto-js): Use WeakRef to avoid calling free manually
feat(crypto-js): Use `WeakRef` to avoid calling `free` manually
2022-10-31 11:06:45 +01:00
Ivan Enderlin 3441c6cf9a feat(crypto-js): Use WeakRef to avoid calling free manually.
By asking `wasm-bindgen` to generate JS glue code for `WeakRef` support,
it removes the need to call `free` manually to free objects, thus we reduce
potential memory leaks inside Rust. See https:// rustwasm.github.io/docs/wasm-
bindgen/reference/weak-references.html to learn more.

It uses [`FinalizationRegistry`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry)
under the hood, I reckon it's quite common and fits into Matrix's clients needs
in terms of browser support.
2022-10-28 21:02:35 +02:00
Ivan Enderlin a07f89e340 feat(crypto-js): OlmMachine.initialize is the new constructor.
While technically a type constructor can return a `Promise`, it's not
considered as idiomatic JavaScript to do that. We are changing `new OlmMachine`
to `OlmMachine.initialize`. The rest of the code is strictly the same.
2022-10-28 20:37:46 +02:00
Benjamin Kampmann cacb20e3ef fix(ffi): Remove duplicate RequireState definition, left over after #1151 (#1155) 2022-10-28 13:38:21 +02:00
Benjamin Kampmann 255d3b7a18 Merge pull request #1154 from matrix-org/stefan/sessionVerificationFixes
Sliding Sync FFI and DevEx fixes
2022-10-28 11:40:33 +02:00
Jonas Platte 6ad7d29364 refactor(sdk): When processing remote events, check for duplicates 2022-10-28 11:30:32 +02:00
Jonas Platte 333c4f0644 refactor(sdk): Move timeline event origin_server_ts and raw_event fields
… from TimelineEventMetadata to Flow because they always exist for
remote events, and never for local echoes.
2022-10-28 11:30:32 +02:00
Jonas Platte 6ce23b17a6 refactor(sdk): Update tracing event for local echo not being found 2022-10-28 11:30:32 +02:00
Benjamin Kampmann 0cb34f7c86 fix: style fixes 2022-10-28 11:01:54 +02:00
Jonas Platte 0d84cf7b74 refactor(sdk): Make Debug format of EventTimelineItem less noisy 2022-10-28 10:56:28 +02:00
Jonas Platte f2ea72224d feat(bindings): Add {TimelineItem,EventTimelineItem}::fmt_debug 2022-10-28 10:56:28 +02:00
Jonas Platte 01cc896dab refactor(bindings): Use uniffi proc-macros a little more in sdk-ffi 2022-10-28 10:55:46 +02:00
Benjamin Kampmann 278c6059ae perf(sliding-sync): run outgoing e2ee requests and sync in parallel.
And continue the loop if even we hit an error in between
2022-10-27 15:59:02 +01:00
Benjamin Kampmann d164165c7c fix(ffi): remove extra to-device processing 2022-10-27 15:57:36 +01:00
ganfra 5a9b75c3ff kotlin-bindings: remove sample 2022-10-27 14:38:17 +02:00
ganfra c7c63fa41c Merge branch 'main' into ganfra/kotlin_bindings 2022-10-27 14:37:53 +02:00
Benjamin Kampmann 0e59078211 fix(ffi): call process_sync_error of client for sliding-sync errors. break if wnated 2022-10-27 12:32:49 +01:00
Benjamin Kampmann 120d5edb03 feat(sliding-sync): Replace anyhow with proper Error type 2022-10-27 12:32:05 +01:00
Benjamin Kampmann afc33e616c revert start_sync removal 2022-10-27 12:06:06 +01:00
Jonas Platte 324411201e fix(sdk): Enable required ruma features for experimental-timeline 2022-10-27 12:56:51 +02:00
Jonas Platte e1ecc9de0d chore: Bump versions of matrix-sdk, matrix-sdk-base
… to bring them up to the released ones from the v0.6.x branch.
All changes from those versions are already present on main.
2022-10-27 12:56:37 +02:00
Jonas Platte c9c4473cd4 ci: Cache xtask for bindings checks 2022-10-27 11:58:42 +02:00
Jonas Platte 41be4c5209 ci: Test bindings generation 2022-10-27 11:58:42 +02:00
Jonas Platte 426f60a6a2 ci: Add bindings check to xtask 2022-10-27 11:58:42 +02:00
Jonas Platte eddf202a76 chore: Upgrade UniFFI 2022-10-27 11:27:28 +02:00
Stefan Ceriu d4cd8710a4 Refactor the SessionVerification flows and get them working through sliding sync 2022-10-27 10:45:05 +03:00
Benjamin Kampmann a443e7277d feat: Add sliding sync timeline events and extensions (#1054)
Add general extension framework for sliding sync, implementing the e2ee, to-device and account-data extensions as per existing proxy implementation. Add a new (ffi exposed) function to use activate the extensions.

Also extends jack-in to have permanent login and storage support now, rather than posting an access token and expose messages inside the sliding-sync layer to actually use the decrypted messages if given. Contains a lot of fixes around these aspects, too, like uploading any remaining messages from the olm-machine on every sliding-sync-request or processing even if no room data is present (which can happen now as processing only extensions might takes place).
2022-10-25 13:42:43 +02:00
Jonas Platte 2232092f11 refactor(sdk): Move timeline event handling fns to TimelineInner 2022-10-25 13:03:52 +02:00
Damir Jelić 2547d6ed64 fix(crypto): Don't create an infinite amount of streams in the SAS examples 2022-10-25 13:00:27 +02:00
Damir Jelić 6cb37520ce docs(bindings): Document the EncryptionSettings a bit better 2022-10-25 12:44:18 +02:00
Jonas Platte 50a5ec89e9 chore: Fix more clippy lints 2022-10-25 12:20:07 +02:00
Jonas Platte 285dc129c3 chore(crypto): Clean up SecretInfo::as_key implementation 2022-10-25 12:20:07 +02:00
Jonas Platte a59fdc08bb chore: Fix clippy lints
Automated with cargo clippy --fix.
2022-10-25 12:20:07 +02:00
Jonas Platte 9de98dfa65 fix(sdk): Remove token field from SyncSettings Debug output 2022-10-24 13:14:30 +02:00
Damir Jelić c03c90c1cf feat(bindings)!: Allow passing the E2EE settings when sharing a room key 2022-10-24 09:50:57 +02:00
Damir Jelić 4a6208f808 feat(crypto): Add signaling to the SAS verification
This patch adds a way for users to listen to changes in the state of a
SAS verification.

This makes it much more pleasant to go through the verification flow and
incidentally easier to document it.
2022-10-24 09:50:17 +02:00
Jonas Platte 1aa48beca9 Revert "refactor(bindings): Use new uniffi::Enum derive macro in crypto-ffi"
This reverts commit 6b9075aa42.
2022-10-20 17:41:16 +02:00
Jonas Platte e05444554d chore: Fix typo in comment 2022-10-20 14:14:18 +02:00
Jonas Platte 411095425c refactor(sdk): Make ClientBuilder::{sled_store, indexeddb_store} simple setters 2022-10-20 13:35:06 +02:00
Jonas Platte 6b9075aa42 refactor(bindings): Use new uniffi::Enum derive macro in crypto-ffi 2022-10-20 12:53:09 +02:00
Jonas Platte 01dc166293 chore: Upgrade UniFFI 2022-10-20 12:53:09 +02:00
Jonas Platte 584a82b19b fix(sdk): Remove Debug implementation for SessionTokens 2022-10-20 11:25:54 +02:00
Jonas Platte 0aba22855b fix(sdk): Don't include access and refresh token in Debug output of Session 2022-10-20 11:25:54 +02:00
Jonas Platte a50743874f chore(base): Merge consecutive impls for the same type 2022-10-20 11:25:54 +02:00
Flix 7851eefb61 refactor(base)!: Get rid of String in StoreError 2022-10-19 16:28:27 +02:00
Stefan Ceriu 8470b494f4 chore(bindings): Replace various expectations with anyhow contexts 2022-10-19 13:18:19 +02:00
Andy Uhnak c92d946777 Set local trust 2022-10-19 09:38:16 +02:00
Jonas Platte 0cfc7540cf refactor: Use workspace dependencies for tracing 2022-10-18 19:24:22 +02:00
Jonas Platte 6990c1ca5c test(base): Use tracing-subscriber for test debugging
… same as in our other crates.
2022-10-18 19:24:22 +02:00
Jonas Platte 0ee63344da chore(base): Sort dev-dependencies 2022-10-18 19:24:22 +02:00
Jonas Platte 3daaa457e1 fix(bindings): Make tracing dependency for crypto-js optional 2022-10-18 19:24:22 +02:00
Jonas Platte 7a826fb7d6 ci: Cancel all CI jobs for old commits when pushing to a PR branch 2022-10-18 17:06:42 +02:00
Jonas Platte 95dfedb70a Another test commit 2022-10-18 16:52:01 +02:00
Jonas Platte eb58dcc556 Try to make it work 2022-10-18 16:51:07 +02:00
Damir Jelić 57e9b36fac chore(bindings): Don't mention that the OlmMachine is a memory-only one 2022-10-18 15:49:09 +02:00
Damir Jelić 90865e2a0a chore(bindings): Clarify the OlmMachine constructor in the WASM bindings 2022-10-18 15:49:09 +02:00
Damir Jelić 52d96ceb60 fix(bindings): Allow setting the store without a passphrase 2022-10-18 15:49:09 +02:00
Damir Jelić 8a720af215 feat(crypto): Support transitioning from a QR verification into a SAS or scanning QR one 2022-10-18 15:18:51 +02:00
Jonas Platte bac95f4494 ci: Upgrade cancel-workflow-action 2022-10-18 15:00:35 +02:00
Jonas Platte 31293c6c2b Testing with empty commit 2022-10-18 14:39:47 +02:00
Jonas Platte 089f92d238 ci: Cancel all CI jobs for old commits when pushing to a PR branch
(not just jobs from the ci workflow file)
2022-10-18 14:23:21 +02:00
Andy Uhnak 1a466eb667 Add flow_id to logs 2022-10-18 13:09:12 +01:00
Andy Uhnak 8bbdd28a8b Use cfg-if and debug logs 2022-10-18 13:09:11 +01:00
Andy Uhnak 61452ad190 Replace QR with SAS verification 2022-10-18 13:08:47 +01:00
Flix 4816d93e96 docs: Improve documentation for custom event handler context 2022-10-18 13:39:08 +02:00
Jonas Platte f25af209e1 refactor: Use workspace dependencies for zeroize 2022-10-18 13:38:05 +02:00
Jonas Platte e6891addfb refactor: Use workspace dependencies for vodozemac 2022-10-18 13:38:05 +02:00
Jonas Platte f57b7782f4 refactor: Use workspace dependencies for ruma 2022-10-18 13:38:05 +02:00
Jonas Platte e91cee7154 fix(sdk): Always send an access token for get_profile 2022-10-18 13:31:59 +02:00
Jonas Platte 5b46fa73e1 fix(sdk): Always send an access token for get_display_name 2022-10-18 11:19:32 +02:00
Jonas Platte b309441dec fix(sdk): Don't log the access token in HttpClient::send 2022-10-17 20:04:49 +02:00
Jonas Platte 391efcec12 feat(appservice): Return a concrete type from AppService::service 2022-10-17 17:03:37 +02:00
Jonas Platte 4e583bcb8a chore: Exclude xtask from tarpaulin coverage collection 2022-10-17 15:38:42 +01:00
Benjamin Kampmann 5e621b7132 fix!: Switch to uniffi 0.21.0 and workspace-wide dependencies
Upgrade MSRV to 1.64, the first stable release to support workspace-wide depenendencies:
https://blog.rust-lang.org/2022/09/22/Rust-1.64.0.html#cargo-improvements-workspace-inheritance-and-multi-target-builds
2022-10-17 15:38:04 +01:00
Benjamin Kampmann ab796cb32c ci(xtask): switch to using uniffi as a lib rather than globally installed binary 2022-10-17 15:38:04 +01:00
Richard van der Hoff dd82e0b8fc chore: Update bindings/README 2022-10-17 13:24:09 +00:00
Doug 7125730917 ci(xtask): Switch to xtask for swift; run swift tasks on macOS
Merge pull request #1023 from matrix-org/doug/swift-linux: Run Swift tests on macOS
2022-10-17 14:12:43 +02:00
Jonas Platte db2771bd17 feat(bindings): New timeline API 2022-10-17 13:40:35 +02:00
Jonas Platte 95face4aa4 chore(bindings): Delete unused code left over from old timeline API 2022-10-17 13:40:35 +02:00
Jonas Platte a7cb80df1b refactor(appservice): Clean up implementation of user_id_is_in_namespace 2022-10-13 12:13:56 +02:00
ganfra e7d5b44343 Kotlin bindings: add matrix-rust-sdk.aar file to sample 2022-10-12 18:24:30 +02:00
ganfra 47478702b2 Kotlin bindings: create build_sdk script 2022-10-12 18:24:30 +02:00
ganfra 648895e2b0 Kotlin bindings: add uniffi.toml 2022-10-12 18:24:30 +02:00
ganfra c8bddea6e1 Kotlin bindings: update Cargo.toml for kotlin generation 2022-10-12 18:24:30 +02:00
ganfra f42215180e Kotlin bindings: create project with sample, crypto and sdk modules 2022-10-12 18:24:12 +02:00
Jonas Platte 1e77e91ec4 refactor(appservice): Replace warp with axum 2022-10-12 17:32:31 +02:00
Jonas Platte 95613be6c9 chore: Remove redundant cfg attribute 2022-10-12 17:32:31 +02:00
Jonas Platte b83e6be01e refactor(sdk): Use lower-level libraries for builtin SSO server 2022-10-12 17:32:31 +02:00
Damir Jelić 086b6127e2 feat: Improve the timeline example 2022-10-12 16:12:06 +02:00
Jonas Platte 54fd40d8f5 feat(sdk): Add new timeline API 2022-10-12 16:12:06 +02:00
Damir Jelić 67d968d4fa refactor(crypto): Remove the device ID from the megolm v2 m.room.encrypted content 2022-10-12 14:37:05 +02:00
Damir Jelić 7e14857239 test(crypto): Test that megolm v2 key forwards work 2022-10-12 14:37:05 +02:00
Damir Jelić 2425cd99cf fix(crypto): Introduce the proper megolm v2 forwarded room key content 2022-10-12 14:37:05 +02:00
Damir Jelić 7c97c27441 feat(crypto): Start sending out megolm v2 room key requests 2022-10-12 14:37:05 +02:00
Damir Jelić cbadd90eff feat(crypto): Start responding to megolm v2 room key requests 2022-10-12 14:37:05 +02:00
Jonas Platte d18b57dcdd refactor: Make use of as_ruma_api_error internally 2022-10-12 12:34:09 +02:00
Jonas Platte 23c5929cfa feat(sdk): Add Error::as_ruma_api_error 2022-10-12 12:34:09 +02:00
Jonas Platte 7bfa622a57 feat(sdk): Add HttpError::as_ruma_api_error 2022-10-12 12:34:09 +02:00
Jonas Platte f4e0cab11f feat(sdk)!: Merge HttpError::UiaaError into HttpError::Api 2022-10-12 12:34:09 +02:00
Jonas Platte f07735291e Fix warnings from generated code 2022-10-10 19:24:56 +02:00
Anderas d4e7076e1d Compile Crypto FFI for MacOS 2022-10-10 18:59:25 +02:00
Jonas Platte 61e95abaf2 refactor: Use Box::default
… as suggested by clippy.
2022-10-10 18:52:59 +02:00
Damir Jelić 8763a2468c fix(crypto): Take more data into account when comparing sessions 2022-10-10 15:20:25 +02:00
Damir Jelić 8f6e3033e3 fix(crypto): Check for existing room keys when receiving a new one.
Now that we're not scoping the room keys by the Curve25519 sender key
we're opening the door of multiple devices trying to insert the same
room key into our store.

This patch changes our logic so we only store room keys from an
m.room_key event if we don't have one already or if the new key is
a better version of the one we already have.

This mostly assumes that the first room key with a given session id
is coming from the creator of the room key.
2022-10-10 15:20:25 +02:00
Damir Jelić 2f94886663 refactor(crypto)!: Don't use the Curve25519 sender key to store room keys 2022-10-10 15:20:25 +02:00
Jonas Platte 8b728d4ada ci: Pin typos version
Currently, master is broken: https://github.com/crate-ci/typos/issues/590
2022-10-06 17:27:55 +02:00
Jonas Platte 26faf0d4c0 feat(bindings): Add custom kotlin package name for crypto-ffi 2022-10-06 17:27:55 +02:00
Jonas Platte a3113bd125 refactor(bindings): Start using #[uniffi::export] for matrix-sdk-crypto-ffi 2022-10-06 17:27:55 +02:00
Jonas Platte a75d7171f8 refactor(bindings): Rename crypto-ffi UniFFI namespace
… from olm to matrix_crypto_ffi to match the lib name.
Names need to match for integration of UniFFI's proc-macro frontend.
2022-10-06 17:27:55 +02:00
Jonas Platte c3eaa864dd refactor(bindings): Unset custom lib name for crypto-ffi 2022-10-06 17:27:55 +02:00
Jonas Platte 6a561c20e2 refactor(bindings): Use uniffi::export for most of ClientBuilder's API 2022-10-06 10:56:22 +02:00
Jonas Platte 0581b8a884 refactor: Derive Default for enums 2022-10-06 10:55:49 +02:00
Jonas Platte bdfdaeb0ae chore: Upgrade async-once-cell 2022-10-06 10:55:49 +02:00
Jonas Platte c24c20f7f1 Raise MSRV to 1.62 2022-10-06 10:55:49 +02:00
Jonas Platte 6b3acf1bf7 chore(sdk): Use inline tables more in Cargo.toml
Makes the file a little more comprehensible.
2022-10-06 10:55:49 +02:00
Jonas Platte 822cdae8f3 chore(sdk): Only include derive_builder dependency for sliding-sync feature 2022-10-06 10:55:49 +02:00
Jonas Platte 4fcbb61145 refactor!: Delete the store module
It was very confusing that matrix_sdk::store::make_store_config's first
argument was either a path or a database name depending on the build
configuration. ClientBuilder::{sled_store, indexeddb_store} are also
easier to use.
2022-10-05 13:49:12 +02:00
Jonas Platte 4628481d0e refactor(bindings): Use UniFFI proc-macro frontend more 2022-10-05 12:21:44 +02:00
Jonas Platte e05de8d4ce chore: Upgrade UniFFI 2022-10-05 12:21:44 +02:00
Johannes Marbach 6bc79b04cf feat(bindings): Allow clients using matrix-sdk-ffi to set the user agent 2022-10-05 09:18:52 +02:00
Damir Jelić fa88751548 fix(crypto): Ignore duplicate verification requests 2022-10-04 13:38:07 +02:00
Andy Uhnak 09c906b1d7 Debug message 2022-10-04 11:50:16 +01:00
Andy Uhnak 6fab6ef318 Ignore duplicate verification requests 2022-10-04 11:38:08 +01:00
Lautaro Bustos d5728f235d feat(sdk): Allow configuring the presence in the SyncSettings 2022-10-03 11:52:23 +02:00
Johannes Marbach 6827c70886 chore: Ignore Swift's .build folder 2022-09-30 21:54:09 +02:00
Jonas Platte ab1a6a6b37 chore: Remove experimental-timeline Cargo feature 2022-09-30 13:48:17 +02:00
Jonas Platte 3c9c7290ae chore: Remove deprecated functions 2022-09-30 10:40:56 +00:00
Benjamin Kampmann 530f8057cc Merge pull request #1071 from matrix-org/ben-releasing-0.6
chore: crates.io only accepts versioned git deps
2022-09-29 15:02:10 +02:00
Ivan Enderlin 8e0e752f12 Merge pull request #1075 from Hywan/fix-crypto-nodejs-release-script-npm-token
fix(crypto-nodejs): Hopefully it will make `NPM_TOKEN` available in the script
2022-09-29 11:52:30 +02:00
Ivan Enderlin b1493c5217 fix(crypto-nodejs): Hopefully it will make NPM_TOKEN available in the script.
We hope that adding this to the workflow will make `NPM_TOKEN`
available from within the script.
2022-09-29 11:36:29 +02:00
Ivan Enderlin 7988dc6cf3 Mergfix(crypto-nodejs): Fix the prep release script
fix(crypto-nodejs): Fix the prep release script
2022-09-28 17:48:43 +02:00
Ivan Enderlin 180796c747 fix(crypto-nodejs): Fix the prep release script. 2022-09-28 17:46:56 +02:00
Benjamin Kampmann fefd2a6b68 chore: crates.io only accepts versioned git deps 2022-09-28 17:15:09 +02:00
Benjamin Kampmann 269bf3a706 chore: remove old release notes 2022-09-28 17:07:37 +02:00
Benjamin Kampmann 91d1ee9f09 chore: fix typos in Upgrade guide 2022-09-28 17:07:37 +02:00
Benjamin Kampmann b22fe63476 chore: More minor changes of note 2022-09-28 17:07:37 +02:00
Benjamin Kampmann 92467af4a0 docs: Upgrading typo fixes 2022-09-28 17:07:37 +02:00
Benjamin Kampmann f1f1c1bba6 chore: Version bump 2022-09-28 17:07:37 +02:00
Benjamin Kampmann ff82d6420d chore: specify which crates to release and which not 2022-09-28 17:07:37 +02:00
Benjamin Kampmann b9409b7c0f docs: more migrations guide 2022-09-28 17:07:37 +02:00
Benjamin Kampmann ea51935601 docs: Clarifications in the Upgrade guide 2022-09-28 17:07:37 +02:00
Benjamin Kampmann 2fff5d916b docs: More Upgrade guidance 2022-09-28 17:07:37 +02:00
Benjamin Kampmann d16faee68b docs: First outline of upgrades guide 2022-09-28 17:07:37 +02:00
Benjamin Kampmann df9b60a5bc chore: Ignore flaky redaction tests 2022-09-28 17:07:10 +02:00
Damir Jelić 1a57170970 test(crypto): Make sure we don't accept megolm encrypted events over to-device 2022-09-28 16:03:17 +02:00
Damir Jelić 41449d2cc3 test(crypto): Test that we reject forwarded room keys from other users 2022-09-28 16:03:17 +02:00
Damir Jelić 093fb5d0aa fix(crypto): Only accept forwarded room keys from our own trusted devices 2022-09-28 16:03:17 +02:00
Ivan Enderlin 513afa57ef feat(indexeddb): Introduce the experimental-nodejs feature
feat(indexeddb): Introduce the `experimental-nodejs` feature
2022-09-28 14:57:55 +02:00
Ivan Enderlin 25632f04d4 feat(crypto-js): Update to napi-rs 2.9.1
feat(crypto-js): Update to `napi-rs` 2.9.1
2022-09-28 14:52:03 +02:00
Ivan Enderlin 7922786e0c chore: Fix formatting. 2022-09-28 14:34:16 +02:00
Ivan Enderlin 9a006c58f5 feat(crypto-js): Update to napi-rs 2.9.1 2022-09-28 14:30:40 +02:00
Ivan Enderlin 9f4385e639 feat(indexeddb): Introduce the experimental-nodejs feature.
By default, the new `experimental-nodejs` feature is
disabled. However, when enabled, it uses our own fork of
`indexed_db_futures` that make it work on Node.js, and so this entire
`matrix-sdk-indexebdb` crate.
2022-09-28 14:27:37 +02:00
Damir Jelić e79b683560 fix(crypto): Drop the redundant event_type field from ToDeviceEvent<C> 2022-09-28 11:27:44 +02:00
Damir Jelić 27e4675df0 fix(crypto) Fix the zeroizing serialization of to-device events
The crypto crate consumes to-device events, if needed decrypts them, and
in the end reserializes while zeroizing secrets.

It needs to do this reserialization cycle as loslessly as possible, this
is why we have a bunch of BTreeMaps lying in events and contents that
capture unknown fields.

The Deserialize implementation of ToDeviceEvent<C> put the event type
into this BTreeMap but the Serialize implementation figures it out from
the EventType trait of the content.

This patch simplifies things by putting the event type into
ToDeviceEvent<C>, this also means that we can just derive the Serialize
implementation for ToDeviceEvent<C>.
2022-09-27 21:33:06 +02:00
Benjamin Kampmann b5bd6dfee9 fix: Apply redactions to room state events in database, too (#917)
fixes #890
2022-09-27 09:24:53 +02:00
Ivan Enderlin f8b02d1a11 feat(crypto-js): Implement OlmMachine.export_room_keys and .import_room_keys
feat(crypto-js): Implement `OlmMachine.export_room_keys` and `.import_room_keys`
2022-09-27 07:26:51 +02:00
Jonas Platte 0a5ebe4dce feat(sdk): Make EventHandlerDropGuard public (#1055) 2022-09-26 18:18:12 +02:00
Benjamin Kampmann e8278b5d68 feat: add Result return value to sync_* (#1037)
Inspired by the changes in #1013 I was thinking about the use case for `sync_*` and how we handle error cases. Most notably while we give the callback the option to stop the loop, we don't really give an indication to the outside, how to interpret that cancellation: was there a failure? should we restart?

Take e.g. a connectivity issue on the wire, we'd constantly loop and just `warn`, what you might or might not see. Even if you handle that in the `sync_with_result_callback` and thus break the loop, the outer caller now still doesn't know whether everything is honky dory or whether they should restart. 

This Changes reworks that area by having all the `sync` return `Result<(), Error>`, where `()` means it was ended by the inner callback (which in `sync()` never occurs) or `Error` is the error either the inner `result_callback` found or the that was coming from the `send` in the first place. Thus allowing us to e.g. back down to sync as it was a dead wire or restart it if there was only a temporary problem. Making all that a just a bit more "rust-y".
2022-09-26 17:14:15 +02:00
Jonas Platte f054141e66 chore: Revert "feat(sdk): Make room actions return typed room objects" 2022-09-26 14:13:25 +02:00
Jonas Platte 1a6d5e7b26 Revert "feat(sdk): Make room actions return typed room objects"
This reverts commit febfcf9796.
2022-09-26 14:01:52 +02:00
Jonas Platte d3e5a3b7db Revert "test: Add test for repeated joining and leaving"
This partially reverts commit df4ee7db4c.
The changes to logging and ci-start.sh are kept.
2022-09-26 12:31:12 +02:00
Jonas Platte 92809590b1 Revert "feat(examples): Add an example that lets you create rooms"
This reverts commit 01f8ed10aa.
2022-09-26 12:23:08 +02:00
Jonas Platte 00d388f3d3 Revert "feat(sdk): Make room actions return typed room objects"
This reverts commit febfcf9796.
2022-09-26 12:19:36 +02:00
Jonas Platte 59901b6349 Update Cargo.lock 2022-09-26 12:19:36 +02:00
Johannes Becker 914ac5279e chore: Make clippy happy 2022-09-23 17:01:01 +02:00
Johannes Becker c23976f975 refactor(appservice): Move example to top-level 2022-09-23 17:01:01 +02:00
Emelie Graven cb94d60e0e feat(appservice): Add default request config
This adds a default RequestConfig to use when creating new virtual
clients.
2022-09-22 14:25:00 +02:00
Ivan Enderlin df50a5446f test(crypto-js): Test InboundGrounSession.sessionId and .hasBeenImported. 2022-09-22 11:31:28 +02:00
Ivan Enderlin 62ab263d0e test(crypto-js): Test OlmMachine.(en|de)crypt_exported_room_keys. 2022-09-22 11:27:12 +02:00
Emelie Graven 2818551d62 refactor(appservice)!: Use builder pattern 2022-09-22 10:38:17 +02:00
Emelie Graven c29b0f154a chore: Add rust.vim edition workaround 2022-09-22 10:38:17 +02:00
Ivan Enderlin 6fa04a0ba1 feat(crypto-js): Implement OlmMachine.(en|de)crypt_exported_room_keys. 2022-09-21 18:00:15 +02:00
Ivan Enderlin dee14c4ee4 doc(crypto-js): Improve documentation of OlmMachine.import_room_keys. 2022-09-21 17:50:28 +02:00
Ivan Enderlin 5b919dd840 test(crypto-js): Test OlmMachine.import_room_keys. 2022-09-21 17:46:23 +02:00
Ivan Enderlin 406fc58720 feat(crypto-js): Implement OlmMachine.import_room_keys. 2022-09-21 17:37:23 +02:00
Ivan Enderlin b4cec6e1bb feat(crypto-js): Implement OlmMachine.export_room_keys. 2022-09-21 16:03:51 +02:00
Ivan Enderlin 6be0fc1fae feat(sdk): Rename (export|import)_keys to (export|import)_room_keys
feat(sdk): Rename `(export|import)_keys` to `(export|import)_room_keys`
2022-09-21 16:03:29 +02:00
Ivan Enderlin 782b256ea9 test(crypto): Rename OlmMachine.export_keys to OlmMachine.export_room_keys. 2022-09-21 13:43:25 +02:00
Ivan Enderlin 82670022f9 feat(crypto-ffi): Update olm.udl. 2022-09-21 13:24:25 +02:00
Ivan Enderlin b8c41e805b feat(sdk): Rename decrypt_key_export to decrypt_room_key_export. 2022-09-21 13:18:24 +02:00
Ivan Enderlin e3d63c5593 feat(sdk): Rename encrypt_key_export to encrypt_room_key_export. 2022-09-21 13:16:08 +02:00
Ivan Enderlin 0222a8fec2 feat(sdk): Rename OlmMachine.import_keys to .import_room_keys.
Because that's what it does :-).
2022-09-21 13:12:07 +02:00
Ivan Enderlin b442247946 feat(sdk): Rename OlmMachine.export_keys to .export_room_keys.
Because that's what it does :-).
2022-09-21 13:03:42 +02:00
Ivan Enderlin c0ebeee730 feat(crypto-js): Implement OlmMachine.get_identity
feat(crypto-js): Implement `OlmMachine.get_identity`
2022-09-21 10:44:18 +02:00
Stefan Ceriu a46b8f392a Switch ffi layer logs output to stderr instead of stdout 2022-09-21 08:24:09 +02:00
Jonas Platte b12da9d4db refactor!: Move JS-specific functionality behind a Cargo feature
… for matrix-sdk, matrix-sdk-base, matrix-sdk-common and matrix-sdk-crypto.
matrix-sdk-indexeddb as well as the JS bindings and wasm_command_bot are
left as-is because they will likely always require JS.
2022-09-20 14:08:21 +02:00
Stefan Ceriu 6f5681f7c2 feat(bindings): Expose method for fetching media thumbnails 2022-09-19 10:56:22 +00:00
Jonas Platte fee0db03c8 ci: Don't collect coverage for labs 2022-09-19 12:15:13 +02:00
Jonas Platte b2ce906bce ci: Don't report coverage inside test code 2022-09-19 12:15:13 +02:00
Jonas Platte e45d6f45fd refactor: Replace str::to_string with to_owned 2022-09-16 17:45:43 +02:00
Ivan Enderlin 84aa85958c test(crypto-js): Test OlmMachine.getIdentity. 2022-09-16 11:23:32 +02:00
Jonas Platte b941c16b7d chore(sdk): Upgrade derive_builder 2022-09-16 11:17:24 +02:00
Ivan Enderlin 1a5379881e test(crypto-js): Test EventId. 2022-09-16 11:11:06 +02:00
Ivan Enderlin 0c4b85e1f9 feat(crypto-js): Implement OwnUserIdentity and UserIdentity. 2022-09-16 11:02:34 +02:00
Ivan Enderlin 602dbe42f2 doc(crypto): Fix a typo. 2022-09-16 10:57:02 +02:00
Ivan Enderlin d3d316ee6c chore(crypto-js): Add helper to cast from JS Array to Vec<T>. 2022-09-16 10:46:28 +02:00
Jonas Platte 494969ed49 chore: Fix clippy lints 2022-09-15 20:48:22 +02:00
Kévin Commaille 34ed04958f fix(sdk): Export RoomKeyImportError 2022-09-15 18:35:59 +02:00
Jonas Platte 8da456055d refactor: Use event handlers in emoji_verification example 2022-09-15 18:33:58 +02:00
Jonas Platte ef462c786a refactor(sdk): Clean up sync_with_callback implementation 2022-09-15 17:53:32 +02:00
Benjamin Kampmann 1a50f42402 fix(jack-in): limit log4rs features to minimum 2022-09-15 17:08:09 +02:00
Benjamin Kampmann 020a75d55c feat: implement " MSC 3575: sliding sync " behind a feature flag (#728)
* starting with jack-in

* starting by flying tui

* connecting to real server, showing info

* add .env to gitignore

* infrastructure for tests

* display loading time for syncv2

* minor design updates

* initial sync

* finalise first edition of sliding sync

* directly link to sliding sync and show rooms list

* nicer UI, toggle logs

* passing through sliding sync homeserver

* separate syncs and disable v2 autostart

* selecting rooms

* nicer view

* configurable batches and more default needed events

* selecting rooms

* calculate and show status info per room

* precalculated room stats

* restructure code to allow for cancellation of streams

* finish up merge updates

* fix calculation error in room list len

* cleaning up system flow

* fixing sync up

* new multi-view API

* move sliding sync in separate module

* fixing format

* adding and clearning views

* expose filters and timeline limits

* renamed

* adding room subscriptions to sliding sync

* update summary

* live fetching and subscriptions in jack-in

* subscribe to selected room

* starting to switch to tuireal - using example

* status bar and first linkup

* re-adding rooms

* implementing port for customised update event

* showing details and timeline

* fix formatting

* cleaner UI, updating details quickly

* make it green

* implement other Ops

* proper handling of invalidation

* saving sliding sync results to db, too

* saving new prev_batch field if given

* split events and timeline

* cleaning up

* live updates

* upgrading to latest ruma and matrix-sdk

* Update tui-logger to fix the broken build

* fixing latest ruma sliding-sync-branch updates

* feat: first set of ffi sliding sync

* expose sliding sync via FFI

* implement un-/subscribe

* implement view state updates

* updating to latest JSON format and ruma update

* implementing room selecting for new data model

* fixing room selection

* fixing feature flag requirements for sliding-sync

* fixing style, clippy and docs

* style(sliding-sync): fixing rust format styles

* fix(ffi): fixing sliding sync merge failure

* fix(jack-in): update jack-in to latest ruma

* fix(sliding-sync): need to have a version set before polling proxy

* expose sliding sync builder over ffi

* add SlidingSyncViewBuilder

* add forgotten changes on sdk itself

* new file logger feature on jack for deeper logging

* fix(http-client): log the raw request we are sending out

* feat(sliding-sync): better logging

* fix(sliding-sync): switch to full-listen after reaching live state in full-sync and make sure we replace the correct entries

* feat(ffi): expose sliding sync view ranges

* fix(ffi): fixing sliding sync start_sync loop to actually loop

* feat(sliding-sync): allow lookup of room data

* feat(sliding-sync-ffi): fetching name of room

* feat(ffi): expose unread_notifications of rooms

* feat(ffi): stoppable spawn for sliding sync

* fix(ffi): expose has_unread_notifications on room

* feat(sliding-sync): latest room message

* fix(sliding-sync): update to latest ruma layout

* doc(sliding-sync): adding docs to builder fns

* feat(sliding-sync): extra signal on the view to inform about general updates

* fix(sync-v4-ffi): expose new callbacks via ffi

* fix(sliding-sync): reduce default required states to make things faster

* fix(sliding-sync): fix build params

* feat(jack-in): extended instrumentation

* fix(sliding-sync): unbreak faulty feature-gate

* fix(sliding-sync-ffi): mut where mut is due

* fix(sdk): allow separate homeserver on every request to unbreak using send on client while in sliding sync on a proxy

* fix(jack-in): update to latest dependencies, that work

* feat(ffi): helper to patch sliding sync room into regular room

* style(jack-in): cargo fmt

* fix(sliding-sync): Update to latest ruma changes

* fix(sliding-sync): fix missing FFI updates to latest ruma

* feat(sliding-sync)!: simplify stream cancellation, cancel ffi sync if already existing

* fix: timeline backwards pagination must work without synctoken

* fix(sliding-sync): clarify order of messaes in alive TL; pick correct last item

* fix: update view delegate api for clarity

* style(jack-in): fix cargo warnings

* feat(sliding-sync): update room details

* fix(sliding-sync): only update room info selectively when given

* fix(sliding-sync-ffi): convert and store counts as u32, check against 0 for has notificaitons

* style: cargo fmt, file endings and a few other minor style fixes

* docs(jack-in): improving CLI and Readme

* feat(sliding-sync): allow setting of required event_states on viewbuilder

* style(sliding-sync): docs and minor fixes

* style(sliding-sync): various clippy fixes

* style(jack-in): clippy suggestions applied

* fix(sliding-sync): Delegate becomes observer

* test(sdk): adding test for request config

* docs: Fixing copyright header

* style(ffi): Nicer naming of params for observer

* fix(ffi): sliding sync is not optional for now

* fix(sdk): remove superflusous tracing instrumentation

* fix(sdk): use structured logging

* fix(jack-in): removed unneded log import

* fix(jack-in): use server_name rather than deprecated user_id on ClientBuilder

* style: typo and clippy

* style(sliding-sync): clippy and formatting

* fix(sliding-sync): cleaning up minor syntax issues

* fix: remove unneded feature-definition section

* fix(sliding-sync): minor fixes as per review

* fix(sliding-sync): Make Builders owned

* fix(sliding-sync): more minor style improvements

* fix(sliding-sync): minor style improvements

* fix(sliding-sync): remove homeserver from RequestConfig, use specific internal fn instead

Co-authored-by: Stefan Ceriu <stefanc@matrix.org>
2022-09-15 11:45:29 +00:00
Ivan Enderlin f4e0c6e243 feat(crypto-js): Start implementing OlmMachine.get_identity. 2022-09-15 10:32:57 +02:00
Ivan Enderlin 01f1b9b846 feat(crypto-js): Implement EventId. 2022-09-15 10:32:57 +02:00
Ivan Enderlin abcd287496 chore(crypto-js): Simplify code with a lovely macro. 2022-09-15 10:32:57 +02:00
Ivan Enderlin cf96a3ba2e feat(crypto-js): Implement key verification
feat(crypto-js): Implement key verification
2022-09-15 10:13:51 +02:00
Ivan Enderlin 9d2e0fe8ad doc(crypto-js): Fix typos. 2022-09-15 09:59:27 +02:00
Jonas Platte 83d5e567eb feat(bindings): Update send_reply to accept markdown 2022-09-14 22:10:16 +02:00
Benjamin Kampmann a47d8669cd feat: log out facilities
Merge pull request #1013 from matrix-org/ismail/logout

Add logout facilities to `client` and helpers to ffi for tracking the soft-logout issued by a server. Further more this adapts the `sync_with_callback` by adding a new `sync_with_result_callback` that hands the entire `Result` to the callback.
2022-09-14 15:15:47 +02:00
ismailgulek 0643292d76 Fix formatting 2022-09-14 15:07:05 +03:00
ismailgulek 8a26ba8343 Add a warning on sync error 2022-09-14 14:29:30 +03:00
ismailgulek cc1c6aedcb Revert sync_with_callback api call changes 2022-09-14 14:17:30 +03:00
ismailgulek 55cf573142 Implement the new sync with result callback method 2022-09-14 14:10:01 +03:00
Stefan Ceriu c5006081e6 feat(bindings): Add method for sending plain text replies 2022-09-14 10:21:23 +00:00
Ivan Enderlin 4c4fcf91c1 chore(crypto-js): Inline vodozemac dependency. 2022-09-14 10:01:03 +02:00
Ivan Enderlin 6842fb97fd chore(crypto-js): Make Clippy happy. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 8027a3036c test(crypto-js): Remove dependency to canvas. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 1d9ac6e60c chore(crypto-js): Fix typos. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 2f35b2cfc6 feat(crypto-js): QrCodeScan implements Debug. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 7edd6a148c doc(crypto-js): Fix module documentation. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 3b526ea412 doc(crypto-js): Add missing documentation. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 1c50cee5d7 feat(crypto-js): Implement Sas and Qr cancel* methods. 2022-09-14 09:58:09 +02:00
Ivan Enderlin cb95c59194 test(crypto-js): Test m.key.verification.start and .done for QR code. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 792b4581ab feat(crypto-js): Implement Qr.reciprocate and .confirm_scanning. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 58ea598c68 feat(crypto-js): Implement VerificationRequest.scan_qr_code. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 581c537396 test(crypto-js): Properly test `Qr.toQrCode. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 9834b67bd5 feat(crypto-js): QrCode.renderIntoBuffer returns an Uint8ClampedArray. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 6f89d02599 test(crypto-js): Test QrCode.render_into_buffer. 2022-09-14 09:58:09 +02:00
Ivan Enderlin cbb5080837 test(crypto-js): Properly test Qr.toBytes. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 6a05f834a9 test(crypto-js): Test Qr.toBytes. 2022-09-14 09:58:09 +02:00
Ivan Enderlin c7e0b3ee31 test(crypto-js): Testing key verification workflow until QR code generation. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 0d57983e1b feat(crypto-js): Implement VerificationRequest.generate_qr_code. 2022-09-14 09:58:09 +02:00
Ivan Enderlin bbfc076c7f test(crypto-js): Inject bootstrap cross signing keys when setting up machines. 2022-09-14 09:58:09 +02:00
Ivan Enderlin e367d8574d feat(crypto-js): Implement OlmMachine.bootstrap_cross_signing. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 1be17d354d feat(crypto-js): Implement OlmMachine.(export|import)_cross_signing_keys. 2022-09-14 09:58:09 +02:00
Ivan Enderlin e8331cc40c feat(crypto-js): Enable the qrcode feature by default. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 71a2fac46f test(crypto-js): Reorganize the tests a little bit. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 8948333e1e test(crypto-js): Test until m.key.verification.done \o/. 2022-09-14 09:58:09 +02:00
Ivan Enderlin c471a6fb4d test(crypto-js): Split the Key Verification test case into a test suite. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 6239d31bcf test(crypto-js): Test the Emoji and decimals implementations. 2022-09-14 09:58:09 +02:00
Ivan Enderlin b5a8103023 feat(crypto-js): Implement Sas.accept. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 95709bb4b3 test(crypto-js): Test the Sas implementation. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 14f22979c0 feat(crypto-js): Implement VerificationRequest.start_sas. 2022-09-14 09:58:09 +02:00
Ivan Enderlin e6141d8efc test(crypto-js): Continue to test m.key.verification.request and .ready. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 155b187d45 test(crypto-js): Write first tests for key verification. 2022-09-14 09:58:09 +02:00
Ivan Enderlin e659c724cd chore(crypto-js): Some methods have been renamed. 2022-09-14 09:58:09 +02:00
Ivan Enderlin e00b9221b9 feat(crypto-js): Implement Device.request_verification. 2022-09-14 09:58:09 +02:00
Ivan Enderlin b6f01b3cec feat(crypto-js): Implement the Device and UserDevice API. 2022-09-14 09:58:09 +02:00
Ivan Enderlin 53e21e0c26 feat(crypto-js): Start implementation key verification API. 2022-09-14 09:58:06 +02:00
Ivan Enderlin 9155989060 feat(crypto): Simplify code and add documentation. 2022-09-14 09:56:33 +02:00
Doug bb62437369 feat(bindings): Expose redact method to FFI 2022-09-13 23:06:11 +02:00
Damir Jelić 6e6c474bcb chore(base): Bump the lru crate 2022-09-13 16:03:42 +02:00
Damir Jelić 8ba33f6fd3 chore: Bump vodozemac to a released version 2022-09-13 16:03:42 +02:00
ismailgulek 2591bcbca9 Fix PR remarks 2022-09-13 15:47:26 +03:00
ismailgulek 18a8b2f275 Merge branch 'main' into ismail/logout 2022-09-13 13:01:57 +03:00
ismailgulek f4764bbd8a Fix PR comment 2022-09-13 13:01:38 +03:00
Jonas Platte e46e13d1bf chore: Upgrade Ruma 2022-09-13 08:32:59 +00:00
Kévin Commaille 97995b7bf6 fix(sdk): Re-export qrcode encoding and decoding error types 2022-09-12 19:27:30 +02:00
ismailgulek b4f0438c1a Move processing sync errors into a separate method 2022-09-12 18:40:50 +03:00
ismailgulek 076de488e7 Avoid too much indentation 2022-09-12 17:54:55 +03:00
ismailgulek d0d8e38a1d Merge branch 'main' into ismail/logout 2022-09-12 17:38:24 +03:00
Damir Jelić aac41fc82a refactor(crypto): Remove the forwarding chains
These aren't really useful since they can be easily spoofed by any room
key forwarder.
2022-09-12 16:03:05 +02:00
ismailgulek 268ecea7fe Fix new format error 2022-09-12 16:55:03 +03:00
ismailgulek 24be8a8e82 Update tests 2022-09-12 16:29:46 +03:00
ismailgulek 3cc6fe5dea Fix format errors 2022-09-12 15:35:45 +03:00
Ivan Enderlin d1b2bfcaa4 feat(qrcode): Remove decoding QR code from an image
feat(qrcode): Remove decoding QR code from an image
2022-09-12 13:33:42 +02:00
Ivan Enderlin a0edd2f8d8 test(qrcode): Restore image only for one test. 2022-09-12 12:42:18 +02:00
Ivan Enderlin 478529f230 chore: Update Cargo.lock. 2022-09-12 12:28:31 +02:00
Ivan Enderlin 9411801245 feat(qrcode): Remove decoding QR code from an image.
This patch removes the `decode_image` (default) feature for this
`matrix-sdk-qrcode` crate.

First reason is that `rqrr` panics for some particular QR codes, and
we don't want to panic.

Second reason is that it's not a feature that is used. For Element on
iOS and Android, it's very unlikely that every frame from the camera
will be sent to `matrix-sdk-qrcode` to see if it can be decoded. So
it's obvious that an external library is used to read the bytes from
the QR code, that are then sent to `matrix-sdk-qrcode`. For Element on
Web, it's basically the same argument.

This feature is actually used only in our tests to ensure the
generated QR code is valid, but it sometimes fails due to `rqrr`
(cf. First reason).
2022-09-12 12:15:49 +02:00
ismailgulek 361313fbea Merge branch 'main' into ismail/logout 2022-09-12 13:10:21 +03:00
Ivan Enderlin 2a94f575b8 feat(qrcode): Allow VerificationData to receive a flow ID
feat(qrcode): Allow `VerificationData` to receive a flow ID
2022-09-12 12:10:11 +02:00
Ivan Enderlin f9d09b60d5 chore(crypto): Reduce the size of AnyDecryptedOlmEvent.
`AnyDecryptedOlmEvent::Custom` contains at least 440 bytes, while the
second-largest variant (`RoomKey`) contains at least 0 bytes. Let's
box `Custom` so that the size of `AnyDecryptedOlmEvent` stays low.
2022-09-12 11:57:45 +02:00
ismailgulek 03477afb26 Add initial_device_name and device_id parameters to login method 2022-09-12 12:53:28 +03:00
ismailgulek 6b66a1de56 Introduce did_update_restore_token delegate method 2022-09-12 12:52:27 +03:00
ismailgulek 519a005d16 Introduce is_soft_logout flag on Client and did_receive_auth_error delegate method 2022-09-12 12:51:35 +03:00
Ivan Enderlin 7831e0cd89 chore(crypto): Use to_owned instead of to_string. 2022-09-12 09:49:30 +02:00
Ivan Enderlin 70eeffbbb0 doc(qrcode): Update documentation.
Since we have switched to Vodozemac, those values don't need to be
unpadded base64 anymore.
2022-09-12 09:47:38 +02:00
Jonas Platte 831e802dd0 refactor(bindings): Move some more functions and methods out of UDL 2022-09-09 14:30:23 +02:00
Jonas Platte ab0c144f51 chore: Upgrade UniFFI 2022-09-09 14:30:23 +02:00
Jonas Platte dc05c6e2b8 chore: Silence clippy lint 2022-09-09 12:51:57 +02:00
Jonas Platte 3a6397fdba chore: Update Cargo.lock 2022-09-09 12:51:57 +02:00
Jonas Platte 4a481f09d1 fix(base): Make tokio dev-dependency arch-dependent 2022-09-09 12:51:57 +02:00
Ivan Enderlin fa6745bb60 feat(qrcode): DecodingError::Identifier is no longer useful. 2022-09-08 16:14:30 +02:00
Ivan Enderlin cb21d89229 test(qrcode): Removing decode_invalid_room_id.
A room ID can no longer be invalid, it's just a string representing
either a `EventId` (we can validate that but…) or a `TransactionId`
(which is an opaque string, so it can be anything).
2022-09-08 16:12:21 +02:00
Ivan Enderlin 12b1ec5ef9 feat(crypto): VerificationData takes a flow ID, removing a panic.
This patch updates `QrVerification::new_cross`, by passing a flow ID
as an owned `String` to `VerificationData`, thus removing a panic, and
allowing QR code verification to happen outside a room.
2022-09-08 15:43:26 +02:00
Ivan Enderlin aa1a47831a chore(crypto): Use longer variable names. 2022-09-08 15:43:09 +02:00
Ivan Enderlin cc5034f4b9 feat(qrcode): Allow VerificationData to receive a flow ID.
This patch updates `VerificationData` to receive a flow ID,
represented as an owned `String`, instead of an `OwnedEventId`. Why?
Because QR code verification can happen outside a room. In such
scenario, there is no event ID, but a transaction ID, unified behind
the `matrix_sdk_crypto::FlowId` enum. `VerificationData` doesn't
really care about that details. Proof is that `QrVerificationData`
receives an owned `String`, which is then casted into an
`OwnedEventId` to match this API properly; but at the top, it just
receives a string.

This patch brings also a little bit of clean up while editing code
around.
2022-09-08 15:37:01 +02:00
Jonas Platte 3be8c9585d refactor(sdk): Make SyncSettings Debug repr more compact 2022-09-08 12:13:30 +02:00
Jonas Platte d810fa6883 test(sdk): Enable logging for tests 2022-09-08 12:13:30 +02:00
Jonas Platte a744447bb5 test: Respect RUST_LOG in integration-testing 2022-09-08 12:13:30 +02:00
Damir Jelić 9252e2c7a9 refactor(sdk): Fetch the content using the new account data methods
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2022-09-07 13:38:42 +02:00
Damir Jelić 282072b6b0 chore(bindings): Fix a typo 2022-09-07 13:38:42 +02:00
Timothy Hobbs e997da0e72 feat(sdk): Make created_dm_room public 2022-09-07 13:38:42 +02:00
Benjamin Kampmann b3f3d0a95e fix(sdk): proper implementation of stripped info preference
Merge pull request #979 from FlixCoder/cleanup

Fixes a problem, where non-stripped information of the room had predecence in all scenarios of the memory-, sled- and indexeddb-store, thought according to the spec, we prefer the full info only until we've received new stripped info (from another invite). This fixes that to be in line with the spec by removing stripped data when we see a full-event and keep stripped info preferred if found (so, after you joined, stripped data is removed and when you knocked or are invited again, it is preferred), without ever deleting full data. It also adds nice unit- and integration tests to ensure this works as intended.
2022-09-06 15:47:14 +02:00
ismailgulek 42767968ec Implement logout method on Client 2022-09-06 15:56:14 +03:00
Benjamin Kampmann 68a8a214ee Update testing/matrix-sdk-integration-testing/assets/ci-start.sh 2022-09-06 12:42:54 +02:00
Jonas Platte 5adec41b6b test: Use a weak password hash for crypto-store testing
This speeds up the crypto-store tests a lot.
2022-09-05 16:58:12 +02:00
Flix b816307ea9 fix: Listen only to events after sending the request 2022-09-05 16:41:55 +02:00
Flix a182578722 fix: Fix rooms being returned in wrong state and having wrong state 2022-09-05 16:41:55 +02:00
Flix 8b5368ff06 chore: Clean up various mini things 2022-09-05 16:41:52 +02:00
Flix a368caf2b0 test: Add StateStore integration test for stripped/non-stripped 2022-09-05 16:33:31 +02:00
Flix df4ee7db4c test: Add test for repeated joining and leaving 2022-09-05 16:33:31 +02:00
Damir Jelić 321e56cff8 fix(examples): Listen to the done event in the emoji verification example
Nowadays all verifications send a done event, so this is the safer
option.
2022-09-05 15:54:45 +02:00
Damir Jelić a640312503 feat(crypto): Add method to format emojis
This patch adds a method to format a list of emojis in a a terminal
friendly way.

This method was borrowed from weechat-matrix but it's also quite useful
in our own emoji verification example.
2022-09-05 15:54:45 +02:00
Damir Jelić b2452ae92c feat(examples): Add a verbosity flag to the emoji example
Since this example prints out messages to stdout, it becomes quite hard
to see when we ask for user input if all the logging is going on.

This patch adds a standard verbosity flag which disables all logging by
default.
2022-09-05 15:54:45 +02:00
Jonas Platte a25b2d4418 refactor(crypto): Clean up cryptostore_integration_tests macro 2022-09-05 15:44:14 +02:00
Jonas Platte 5f6775f47c ci: Only cancel running CI for previous commits on PRs 2022-09-05 15:01:38 +02:00
Jonas Platte c2a222278d ci: Consistently indent job steps 2022-09-05 15:01:38 +02:00
Jonas Platte 567b230cb7 ci: Cache xtask binary 2022-09-05 15:01:38 +02:00
Jonas Platte 8535c16bc8 ci: Don't require fmt to run before typo check and clippy 2022-09-05 15:01:38 +02:00
Jonas Platte d009d0475e ci: Move appservice and style back into ci workflow
Required for job dependencies.
2022-09-05 15:01:38 +02:00
Jonas Platte 6b8cf3c02a chore: Remove pre-commit configuration and CI job
It was not really being used.
2022-09-05 15:01:38 +02:00
Jonas Platte edfc0cbe20 refactor(sdk): Deprecate ClientBuilder::user_id 2022-09-05 12:43:21 +02:00
Jonas Platte 91186d8a25 doc(sdk): Replace deprecated method in doctest 2022-09-05 11:55:49 +02:00
Jonas Platte 090d67b6ef refactor(sdk): Move module-level documentation inside the module files 2022-09-05 11:55:49 +02:00
Jonas Platte fd4957f533 test(sdk): Address commented-out code in a test 2022-09-05 11:55:49 +02:00
Jonas Platte 08760bd4c0 refactor(sdk): Make base_client field of Client private 2022-09-05 11:55:49 +02:00
Jonas Platte b769827313 refactor(sdk)!: Move media methods from Client to a new type 2022-09-05 11:55:49 +02:00
Jonas Platte 79b5854c83 feat(sdk): Add request_config method to Client 2022-09-05 11:55:49 +02:00
Jonas Platte 38324f6c60 refactor(sdk): Use Client::send in Client::upload 2022-09-05 11:55:49 +02:00
Jonas Platte 33bce0b18d refactor(sdk): Move RoomMember into room module 2022-09-05 11:55:49 +02:00
Jonas Platte 245ecea263 feat(sdk): Add support for AnySyncTimelineEvent in event handlers 2022-09-05 11:55:17 +02:00
Jonas Platte d4ac1bffd0 refactor(sdk): Rename EventKind to HandlerKind
It's somewhat different as MessageLike, OriginalMessageLike and
RedactedMessageLike are not three distinct event kinds in Ruma.
2022-09-05 11:55:17 +02:00
Jonas Platte d6af63e37b refactor(sdk): Run event handlers for the same event concurrently 2022-09-05 11:55:17 +02:00
Jonas Platte 57dde2c4d3 refactor(sdk): Avoid duplicate work and fix event handler call order
Previously, when both a possibly-redacted timeline event handler and a
non-redacted timeline timeline event handler would apply to multiple
events in a sync response, they would individually run for every event
in order. With this change, they will instead both be called
for one event before the next is processed.
2022-09-05 11:55:17 +02:00
Kévin Commaille 6f3813a65f Re-export vodozemac errors in a separate module 2022-09-05 11:44:42 +02:00
Kévin Commaille 433f75ae57 Remove dead error variants 2022-09-05 11:44:42 +02:00
Kévin Commaille 8f0fb08fe7 feat(sdk): Re-export matrix-sdk-crypto errors 2022-09-05 11:44:42 +02:00
Jonas Platte 4f6ff5c0d3 refactor(sdk): Make use of new Default impls from Ruma 2022-09-02 15:03:00 +02:00
Jonas Platte c4d46f233e chore: Upgrade ruma 2022-09-02 15:03:00 +02:00
Damir Jelić faf0ce5007 chore(examples): Remove some empty useless attributes 2022-09-02 14:54:33 +02:00
Damir Jelić c5e6178b70 refactor(examples): Add proxy support to the emoji verification example 2022-09-02 14:54:33 +02:00
Jonas Platte bf9f431e22 refactor(sdk): Use new account_data method internally 2022-09-02 14:09:17 +02:00
Damir Jelić 4b673cfe9c chore: Fix a bunch of clippy warnings 2022-09-02 11:39:06 +02:00
Benjamin Kampmann 0079f1d569 Merge pull request #977 from zecakeh/fix-examples-autojoin
fix(examples): Fix examples that accept invites
2022-09-02 11:14:40 +02:00
Damir Jelić a71292cb16 chore(crypto): Remove a bunch of unnecessary allow deprecated attributes 2022-09-01 17:08:24 +02:00
Damir Jelić cc813b049e fix(crypto): Set the correct algorithm when exporting a room key 2022-09-01 17:08:24 +02:00
Damir Jelić 1b06d8ca51 refactor(crypto): Start using the customized room key request event type 2022-09-01 17:08:24 +02:00
Damir Jelić 4338d5e534 refactor(crypto): Add a customized room key request event type 2022-09-01 17:08:24 +02:00
Jonas Platte 4c98dfb42a feat(bindings): Enable socks proxy support in matrix-sdk-ffi 2022-09-01 15:07:15 +02:00
Jonas Platte dacaef3ddd fix(bindings): Reduce scope of RwLock read lock
Fixes a clippy lint.
2022-09-01 13:40:47 +02:00
Jonas Platte e4267cc4fd refactor(sdk)! Make upload take &[u8] instead of impl Read
The use of `io::Read` wasn't helping since we had to buffer the whole
file in memory anyways, and we are unlikely to get around that in the
near future.
2022-09-01 13:40:47 +02:00
Jonas Platte 16ac69a967 chore: Simplify integration testing macro 2022-09-01 13:11:05 +02:00
Ivan Enderlin 193da88320 feat(crypto): Rename verified and deleted to is_*
feat(crypto): Rename `verified` and `deleted` to `is_*`
2022-09-01 10:50:05 +02:00
Ivan Enderlin 0d6a19e388 chore: verified has been renamed is_verified. 2022-09-01 10:35:51 +02:00
Flix f49e9be905 feat: Expose client of rooms for extensions 2022-09-01 10:01:18 +02:00
Jonas Platte a954518d73 ci: Stop ignoring .lock files in typos config explicitly
Typos now ignores .lock files automatically without any configuration.
2022-09-01 09:33:40 +02:00
Damir Jelić 16d9ed230a chore(examples): Use automatic links for some URLs 2022-08-31 18:43:47 +02:00
Ivan Enderlin 82b647a888 doc(crypto): Fix typos in the documentation
doc(crypto): Fix typos in the documentation
2022-08-31 17:26:56 +02:00
Ivan Enderlin 2e74983c79 chore: Fix other is_verified. 2022-08-31 17:16:05 +02:00
Ivan Enderlin ffebc7c313 doc(crypto): Fix typos in the documentation 2022-08-31 17:09:33 +02:00
Ivan Enderlin 7d1b60a3b1 doc(crypto): Fix a link. 2022-08-31 16:52:00 +02:00
Ivan Enderlin 53c5158eca doc(crypto): Update link to `is_verified. 2022-08-31 16:45:52 +02:00
Ivan Enderlin 3eab9ca8e5 feat(crypto): Rename verified and deleted to is_*. 2022-08-31 16:44:59 +02:00
Doug 36b41ac1c6 chore(bindings): Replace failing test. 2022-08-31 16:22:59 +02:00
Doug 8a8cc5f230 chore(bindings): Use Swift package for tests. 2022-08-31 16:22:59 +02:00
Jonas Platte 96384d9447 feat(bindings): Add account data interaction to sdk-ffi
Co-authored-by: Doug <douglase@element.io>
2022-08-31 14:40:23 +02:00
Stefan Ceriu bb04a1e041 chore(bindings): Fix Xcode project after sdk-ffi namespace change 2022-08-31 10:51:04 +00:00
Jonas Platte d416e64a7e refactor(sdk): Return only content from Account::account_data[_raw]
Since global account data events only consist of the type that is known
to the user anyways, and the content.
2022-08-31 12:19:35 +02:00
Jonas Platte aedf807025 doc(sdk): Add an example for set_account_data 2022-08-31 12:19:35 +02:00
Jonas Platte 77afa26217 feat(sdk): Add account_data[_raw] accessors to Account 2022-08-31 12:19:35 +02:00
Jonas Platte f1a03ececd feat(sdk): Add public set_account_data[_raw] to Account 2022-08-31 12:19:35 +02:00
Jonas Platte f8502720c3 fix(bindings): Pass library file to uniffi-bindgen
… so that functions bridged via #[uniffi::export] are included in the
generated Swift API.
2022-08-31 11:52:37 +02:00
Jonas Platte 84f9414aa4 refactor(bindings): Simplify debug build script 2022-08-31 11:52:37 +02:00
Jonas Platte 9cfddc4c65 refactor(bindings): Update namespace name for matrix-sdk-ffi 2022-08-31 11:52:37 +02:00
Damir Jelić 01f8ed10aa feat(examples): Add an example that lets you create rooms 2022-08-31 11:51:50 +02:00
Damir Jelić 6bb9a7a7d7 docs(crypto): Improve some docs about the forwarded curve chains
Co-authored-by: Denis Kasak <dkasak@termina.org.uk>
2022-08-30 17:14:29 +02:00
Damir Jelić 75fb3b81a7 fix(crypto): Fix the deserialization of exported inbound group sessions 2022-08-30 17:14:29 +02:00
Damir Jelić 504ad39c27 fix(crypto): Fix the deserialization of inbound group group_sessions
Inbound group sessions would fail to be deserialized if there was a
forwarding curve chain, this patch fixes it so we go through base64 when
we deserialize this field.
2022-08-30 17:14:29 +02:00
Benjamin Kampmann d20db7c7c1 Merge pull request #981 from Hywan/fix-crypto-js-npm-publish
chore(crypto-js): Add `npm run pack`
2022-08-30 08:50:39 +02:00
Ivan Enderlin 1bfcc52a1f chore(crypto-js): Add npm run pack.
This patch introduces a new `pack` NPM script, which runs `wasm-pack
pack` behind the scene.

This patch modifies the `publish` NPM script to run the `pack` script
as a pre-script (so… in the `prepublish` script).

Finally, this patch no longer uses `$npm_execpath` as it doesn't work
on Windows. It should be `%npm_execpath%`. It's not obvious to make
scripts interoperable, so we will stick with `npm` for now.
2022-08-29 14:22:39 +02:00
Damir Jelić a8362e389e Document the receive_supported_keys method in the gossiping module 2022-08-29 10:21:04 +02:00
Damir Jelić fe35e7c9fa ci: Test the experimental-algorithms feature of the crypto crate 2022-08-29 10:21:04 +02:00
Damir Jelić 5768188da8 Fix some clippy warnings 2022-08-29 10:21:04 +02:00
Damir Jelić 1e451a92e0 Add some missing docs to the Olm Session 2022-08-29 10:21:04 +02:00
Damir Jelić 12c1b80bc9 Remove a dead code allowed attribute 2022-08-29 10:21:04 +02:00
Damir Jelić 7fda431d5f Remove the usage of the Ruma EventEncryptionAlgorithm 2022-08-29 10:21:04 +02:00
Damir Jelić 337fbb591d Put the new megolm algorithm behind the experimental feature flag 2022-08-29 10:21:04 +02:00
Damir Jelić 8cca01369a Put the new olm algorithm behind a feature flag 2022-08-29 10:21:04 +02:00
Damir Jelić 8cdc609876 Move the EventEncryptionAlgorithm into a more logical place 2022-08-29 10:21:04 +02:00
Damir Jelić 0673ad315f Add support for megolm.v2 forwarded keys 2022-08-29 10:21:04 +02:00
Damir Jelić 3aaf70cb5a Support the new algorithms in the gossiping tests 2022-08-29 10:21:04 +02:00
Damir Jelić b00e963a21 Add support for the new algorithms in the bindings 2022-08-29 10:21:04 +02:00
Damir Jelić 7d98d87c5a Enable the new olm/megolm algorithms 2022-08-29 10:21:04 +02:00
Damir Jelić fb840f73a3 Add support for the m.megolm.v2.aes-sha2 room key content 2022-08-29 10:21:04 +02:00
Damir Jelić e935f59039 Add support for the m.megolm.v2.aes-sha2 algorithm 2022-08-29 10:21:04 +02:00
Damir Jelić 3df6797419 Add support for the m.olm.v1.curve25519-aes-sha2 algorithm 2022-08-29 10:21:04 +02:00
Damir Jelić eaf1f27831 refactor(crypto): Use our own enum for the encryption algorithms 2022-08-29 10:21:04 +02:00
Damir Jelić 748eff40f0 docs(crypto): Improve some docs around event trust states
Co-authored-by: Denis Kasak <dkasak@termina.org.uk>
2022-08-29 09:50:38 +02:00
Damir Jelić 0084117de9 docs(crypto): Fix some spelling and improve a couple of docs
Co-authored-by: Ivan Enderlin <ivan@mnt.io>
2022-08-29 09:50:38 +02:00
Damir Jelić a4af5bf4e1 fix(crypto): Improve the code checking if a decrypted event is trusted
This now takes into account if the room key was imported, i.e. received
as a forward. We also do the check of the Ed25519 key now.
2022-08-29 09:50:38 +02:00
Damir Jelić 27d4228269 fix(crypto): Check the Ed25519 key when we receive an Olm encrypted event 2022-08-29 09:50:38 +02:00
Kévin Commaille dc39c8d96b fix(examples): Fix examples that accept invites
Spawn a task for accepting the invite otherwise the call never returns and sync stops.
2022-08-26 18:15:56 +02:00
Jonas Platte d427632230 chore: Add more backticks in comments 2022-08-25 18:09:13 +02:00
Jonas Platte 6ddc8ba36f refactor!: Rename [Sync]RoomEvent to [Sync]TimelineEvent 2022-08-25 18:09:13 +02:00
Jonas Platte 4be2f3aa04 chore: Upgrade ruma 2022-08-25 18:09:13 +02:00
Jonas Platte f6c404cb32 feat(sdk): Allow event enums to be used as the first event handler argument 2022-08-25 17:19:44 +02:00
Jonas Platte 20a6a16152 refactor(sdk): Split EventHandlerMap into separate maps
One for room-specific event handlers, one for non-room-specific ones.
2022-08-25 17:19:44 +02:00
Jonas Platte 5a1853b0d5 refactor(sdk): Split event_handler module into more files 2022-08-25 17:19:44 +02:00
Jonas Platte 9183f5d4ef refactor(sdk): Move event handler fields from Client into a new struct 2022-08-25 17:19:44 +02:00
Ivan Enderlin 973833c643 feat(crypto-js): Add store configuration to the OlmMachine constructor
feat(crypto-js): Add store configuration to the `OlmMachine` constructor
2022-08-25 11:37:45 +02:00
Ivan Enderlin ce4f7e2254 Merge branch 'main' into feat-crypto-js-store 2022-08-25 11:22:22 +02:00
Jonas Platte 0018749e15 feat(sdk): Allow Raw<_> to be used as the first event handler argument 2022-08-24 18:35:09 +02:00
Ivan Enderlin 9c414bbe9f feat(crypto-js): Implement OlmMachine.sign and .cross_signing_status
feat(crypto-js): Implement `OlmMachine.sign` and `.cross_signing_status`
2022-08-24 09:48:03 +02:00
Jonas Platte 0b8462423a chore: Reduce indirect dependencies of examples 2022-08-23 18:30:44 +02:00
Jonas Platte 2313c099fa chore(bindings): Replace parking_lot RwLock by std RwLock 2022-08-23 18:30:44 +02:00
Ivan Enderlin 85795a92b6 chore(crypto-js): Make Clippy happy. 2022-08-23 17:38:32 +02:00
Ivan Enderlin ef8207fbaa chore(crypto-js): Make Clippy happy. 2022-08-23 17:06:07 +02:00
Jonas Platte f83292fc75 refactor: Start using UniFFI proc-macro frontend 2022-08-23 16:47:08 +02:00
Jonas Platte 5faed2e635 Upgrade UniFFI 2022-08-23 16:47:08 +02:00
Ivan Enderlin 9722a4c415 chore(crypto-js): Use IndexeddbCryptoStore only for wasm32. 2022-08-23 16:46:09 +02:00
Ivan Enderlin 432449b009 chore(crypto-js): Make cargo fmt happy. 2022-08-23 15:08:51 +02:00
Ivan Enderlin 04634d5f39 chore(crypto-js): Fix a typo in an error message. 2022-08-23 15:04:50 +02:00
Ivan Enderlin ef56f76978 doc(crypto-js): Fix a typo. 2022-08-23 15:03:26 +02:00
Ivan Enderlin 527c727e4a test(crypto-js): Improve test cases. 2022-08-23 15:00:39 +02:00
Ivan Enderlin d947448c64 test(crypto-js): Add more test case when store is not configured correctly. 2022-08-23 15:00:39 +02:00
Ivan Enderlin d497883669 doc(crypto-js): Add more docmuentation and more error messages. 2022-08-23 15:00:39 +02:00
Ivan Enderlin 7f07ac52ef feat(crypto-js): Add store configuration to the OlmMachine constructor.
The `OlmMachine` constructor now has 2 more optional arguments:
`store_name` and `store_passphrase`, to use Indexed DB as the backend
to store the Olm machine keys, rather than having an in-memory Olm
machine.

Node.js is used to test our binding. Indexed DB is absent of
Node.js. So, firstly, we are using the `fake-indexeddb` JavaScript
library to mimic the same API inside Node.js. And, secondly, we use
our own fork of the `indexed_db_futures` Rust crate to provide add
support for Node.js too ([PR is
here](https://github.com/Alorel/rust-indexed-db/pull/11)). It
basically looks in the Node.js global environment if the `indexedDB`
getter is present, just like it is already done for `Window` on any
browser, or `WorkerGlobalScope` for workers.
2022-08-23 15:00:33 +02:00
Jonas Platte 9462061a5a chore: Upgrade ruma 2022-08-23 14:28:21 +02:00
Ivan Enderlin d508237078 chore(crypto-js): Add a release workflow for crypto-js
chore(crypto-js): Add a release workflow for `crypto-js`
2022-08-23 10:49:29 +02:00
Ivan Enderlin 331f8b381f doc(crypto-js): Update documentation for the release workflow. 2022-08-22 15:10:01 +02:00
Ivan Enderlin 2481618608 chore(crypto-js): Create the Github release with the NPM package attached.
This patch updates the `publish` NPM script to explicitly run
`wasm-pack pack` to create the NPM package in the `pkg/`
directory. This patch also updates the Github Action workflow for the
`matrix-sdk-crypto-js` release, to create a new Github Release, with
the NPM package attached as an asset file.
2022-08-22 14:37:09 +02:00
Jonas Platte dd78a8cecd refactor(sdk)!: Make room type constructors private
… and avoid unnecessary clones before calling them.
2022-08-22 13:16:05 +02:00
Ivan Enderlin 39077b185b chore(crypto-js): Add a release workflow for crypto-js.
This patch adds a new `npm run publish` script that:

1. Run `npm run prepublish` (which runs the `build` and `test` scripts),
2. Run `wasm-pack publish`.

Note 1: The `prepublish` script is using the `$npm_execpath`
environment variable instead of just “`npm`”, so that if someone is
using `yarn` or another JavaScript package manager, it _should_ work
(not tested yet).

Note 2: `wasm-pack publish` is run without running `wasm-pack login`
before that. _But_ we are updating the registry URL in the NPM
configuration file in the Github Action workflow, see below.

This patch then creates a new Github Action workflow that is triggered
when a new tag of the form `matrix-sdk-crypto-js-v[0_9]+.*` is
pushed. This workflow runs `npm run publish` basically, but before
that, it updates the NPM configuration file by setting a value for
`//registry.npmjs.org/:_authToken`. Thus, running `wasm-pack login` is
not necessary.
2022-08-22 12:14:44 +02:00
Benjamin Kampmann 68107e6285 Merge pull request #963 from gnunicorn/ben-update-ruma
chore: Update ruma
2022-08-18 16:13:15 +02:00
Benjamin Kampmann 549c829000 chore: Update ruma 2022-08-18 15:52:08 +02:00
Jonas Platte 3581d83389 chore: Upgrade Ruma 2022-08-17 10:15:05 +02:00
Jonas Platte 260b604615 refactor(sdk)!: Split strongly-typed state event functions into two
One for empty state keys under the existing name, one for non-empty
state keys with a `_for_key` suffix.
2022-08-17 10:15:05 +02:00
Ivan Enderlin a1dca23c3c Merge pull request #958 from Hywan/feat-crypto-js-attachment 2022-08-17 10:12:04 +02:00
Benjamin Kampmann 9fd5a8b3b1 Merge pull request #960 from matrix-org/poljar/delete-device-log-improvement
Improve the log line when we think our device has been deleted
2022-08-17 09:46:26 +02:00
Damir Jelić 180822dd18 chore(crypto): Improve the message when we think that we have been deleted 2022-08-16 18:32:14 +02:00
Damir Jelić 20477ab7da chore(crypto): Log our identity keys when we think our device has been deleted 2022-08-16 18:31:30 +02:00
Damir Jelić 06f39696d3 chore(crypto): Improve some logs in the Olm message handling code 2022-08-16 16:18:50 +02:00
Damir Jelić 6f5443f8a0 chore(crypto): Log some info when we try to create an Olm session from a pre-key message 2022-08-16 16:18:50 +02:00
Ivan Enderlin 45a66f3321 doc(crypto-nodejs): Fix a typo. 2022-08-16 16:06:04 +02:00
Ivan Enderlin 7f35691236 feat(crypto-js): Implemented the Attachment API.
Implement the `Attachment.encrypt` and `Attachment.decrypt` methods,
along with its `EncryptedAttachment` companion.

The trick is to avoid copies as much as possible. Instead of dealing
with `Uint8Array` as I've initially done, `&[u8]` and `Vec<u8>` is
passed instead. `wasm-bindgen` is smart enough to do as few copies as
possible (from JavaScript to Wasm's memory is required, and we don't
want to do more), while typing everything as `Uint8Array`.

`EncryptedAttachment.encryptedData` returns a copy of the data
everytime it is called, and that's the last copy I'm not happy with.
2022-08-16 15:56:36 +02:00
Benjamin Kampmann e8d51a4cba Merge pull request #956 from gnunicorn/ben-fix-encode-key
fix(sled): Remove unused `from` on EncodeUnchecked
2022-08-16 15:51:36 +02:00
Benjamin Kampmann 7feffec9c0 fix(sled): feature-gate EncodeUnchecked::from 2022-08-16 15:18:46 +02:00
Ivan Enderlin 8e74834342 doc(crypto-js): Add missing module documentation. 2022-08-16 12:31:19 +02:00
Ivan Enderlin 070637b0ef chore(crypto-js): Fix style. 2022-08-16 12:21:02 +02:00
Ivan Enderlin 2dffe03c8d feat(crypto-js): Implement OlmMachine.sign. 2022-08-16 12:16:01 +02:00
Ivan Enderlin 3f0509e7b1 fix(crypto-js): Add missing Debug impl. 2022-08-16 12:16:01 +02:00
Ivan Enderlin 2275643ea0 chore(crypto-js): Move Vodozemac types into their own Rust module. 2022-08-16 12:16:01 +02:00
Ivan Enderlin 0fa6ff6955 feat(crypto-js): Add DeviceKeyId, DeviceKeyAlgorith and DeviceKeyAlgorithmName. 2022-08-16 12:16:01 +02:00
Ivan Enderlin 997a6ed0ad feat(crypto-js): Implement OlmMachine.cross_signing_status. 2022-08-16 12:16:01 +02:00
Ivan Enderlin fd220f197b doc: Add link to online documentation for crypto-js and crypto-nodejs
doc: Add link to online documentation for crypto-js and crypto-nodejs
2022-08-16 10:21:39 +02:00
Ivan Enderlin 655e62814b doc(crypto-nodejs) Add link to online documentation. 2022-08-16 10:06:22 +02:00
Ivan Enderlin c2468b2f2e doc(crypto-js) Add link to online documentation. 2022-08-16 10:04:54 +02:00
Benjamin Kampmann e0ae4f60e3 docs(examples): update instructions for wasm_command_bot example
Merge pull request #952 from chrisguida/chrisguida/wasm-bot-readme -
2022-08-16 09:48:33 +02:00
Chris Guida 2beb13cc3e update instructions for wasm_command_bot example 2022-08-15 17:56:54 -05:00
Damir Jelić 78dcff9259 chore(crypto): Fix an indentation issue
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2022-08-15 11:39:43 +02:00
Damir Jelić 0bd58a7741 refactor(crypto): Simplify the Curve25519 check when fetching a device
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2022-08-15 11:39:43 +02:00
Damir Jelić 405c3938b7 refactor(crypto): Use the Curve25519 key type in more places 2022-08-15 11:39:43 +02:00
Damir Jelić bd5ea8b0f7 fix(crypto): Fix some error messages 2022-08-15 11:39:43 +02:00
Jonas Platte 24c042e974 refactor(base): Deserialize events at the expected type
… rather than deserializing at an enum type and taking the same branch
for getting a different enum variant¹ as failed deserialization.

¹ which should be impossible anyways
2022-08-15 11:05:22 +02:00
Jonas Platte d236cde0c7 refactor(base): Use StateStoreExt in a few places 2022-08-15 11:05:22 +02:00
Jonas Platte d2f39bc18f feat(base): Add StateStoreExt for statically-typed event retrieval 2022-08-15 11:05:22 +02:00
Jonas Platte 96165f5602 chore(bindings): Use ? operator instead of and_then chaining 2022-08-15 11:05:22 +02:00
Damir Jelić 4a13b8d207 ci: Ignore thead when checking for spelling 2022-08-14 10:20:28 +02:00
Damir Jelić c9c09adb38 chore: Fix some newly detected typos 2022-08-14 10:20:28 +02:00
Jonas Platte aedbbcdde7 fix(sdk)!: Remove SyncEvent implementation for InitialStateEvent
Initial state events aren't actually received through sync, they're sent from
the client to the server when creating a room.

This change makes it impossible to register event handlers for initial state
events. Previously, this was allowed, but the event handler was never called.
2022-08-13 20:21:28 +02:00
Jonas Platte 97f37acb4a chore: Reduce indirect dependencies of sled-state-inspector 2022-08-13 13:08:11 +02:00
Damir Jelić 0b5bfeadea chore(crypto): Add a missing semicolon
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2022-08-13 09:58:57 +02:00
Damir Jelić 6d60acfff4 chore(common): Fix a clippy warning 2022-08-13 09:58:57 +02:00
Damir Jelić 3d56af442d refactor(crypto): Utilize the decrypted Olm event type more
This patch adds some more stronger guarantees that received room key
events and other similarly security critical event types are only
handled if they have been received over a secure Olm channel.
2022-08-13 09:58:57 +02:00
Damir Jelić ae18b01c25 refactor(crypto): Use the SigningKeys collection for inbound group sessions 2022-08-11 16:43:42 +02:00
Damir Jelić 4692a12cd7 refactor(crypto): Create a custom collection type for signed keys 2022-08-11 16:43:42 +02:00
Jonas Platte 344309c1bf chore: Track Cargo.lock to make binding staticlibs reproducible 2022-08-11 13:32:32 +02:00
Jonas Platte 87094a9111 chore: Document dependency upgrade issues 2022-08-11 13:16:10 +02:00
Jonas Platte adf3f9d434 chore(appservice): Upgrade serde_yaml 2022-08-11 13:15:57 +02:00
Jonas Platte 158bd24b40 chore: Bump pprof dependency 2022-08-11 13:09:09 +02:00
Jonas Platte 8e368d86b7 chore: Use the latest git version of UniFFI 2022-08-11 12:56:17 +02:00
Jonas Platte 0fe714df86 chore: Upgrade sled-state-inspector dependencies 2022-08-11 11:59:50 +02:00
Jonas Platte 936f0371de chore: Use once_cell instead of lazy_static in integration test crate 2022-08-11 10:45:04 +02:00
Jonas Platte ecc800a319 chore: Sort dependencies of integration test crate 2022-08-11 10:32:12 +02:00
docweirdo febfcf9796 feat(sdk): Make room actions return typed room objects
… by waiting for the corresponding event confirming the action. Affects:

* Creating a room
* (Re-)Joining a room
* Leaving a room
* Accepting an invitation
* Rejecting an invitation
2022-08-10 17:32:38 +02:00
Damir Jelić 3f0a68082a feat(crypto): Add a setting to only send room keys to trusted devices 2022-08-10 13:31:15 +02:00
Damir Jelić 18861bb595 refactor(crypto): Add a dedicated error for inbound group session exporting 2022-08-10 13:28:54 +02:00
Damir Jelić 329d461a2f feat(crypto): Add customized event type for the forwarded room key 2022-08-10 13:28:54 +02:00
Jonas Platte 4914c595e9 fix(sdk): Further relax event / notification handler bounds on WASM
This allows capturing non-`Send` / -`Sync` values in handler closures.
2022-08-10 12:54:36 +02:00
Jonas Platte ad80839ffd fix(sdk): Make event handler futures non-Send on wasm 2022-08-09 14:25:57 +02:00
Jonas Platte 0701561e45 feat(common): Add SendOutsideWasm, SyncOutsideWasm 2022-08-09 14:25:57 +02:00
Jonas Platte 25030780b0 test(sdk): Run event handler tests on wasm 2022-08-09 14:25:57 +02:00
Jonas Platte 327a404d60 test(sdk): Move test client creation into separate module 2022-08-09 14:25:57 +02:00
Damir Jelić 06e096f6cc fix(examples): Fix the in-room emoji verification example 2022-08-09 14:04:11 +02:00
Damir Jelić a025163dae test(crypto): Test that we correctly preserve relations in an encryption cycle 2022-08-09 14:04:11 +02:00
Damir Jelić 19fcff56de chore(crypto): Make the relation copying code when decrypting a bit simpler 2022-08-09 14:04:11 +02:00
Damir Jelić edf81fb325 fix(crypto): Correctly copy over the relation when encrypting 2022-08-09 14:04:11 +02:00
Damir Jelić 08338acac2 fix(crypto): Fix the deserialization of relations 2022-08-09 14:04:11 +02:00
Kévin Commaille 52e70f1955 refactor(sdk): Don't require the whole session for sending
Allow to send requests when only the access token is available.

Remove the unreachable UserIdRequired error.
2022-08-09 12:26:33 +02:00
Jonas Platte e788ec6b07 ci: Only test one node.js version on macOS 2022-08-08 18:06:54 +02:00
Jonas Platte d3d108deb8 chore: Add missing copyright headers 2022-08-08 16:28:48 +02:00
Jonas Platte bd65d9b7a6 doc(sdk): Remove usage of deprecated function in doctest 2022-08-05 16:16:41 +02:00
Jonas Platte 230bb67763 refactor(sdk): Remove Clone requirement on event handlers 2022-08-05 16:16:41 +02:00
Jonas Platte 10a37f6d51 chore(sdk): Add EventHandlerDropGuard
It isn't used for now, but will be soon.
2022-08-05 16:16:41 +02:00
Jonas Platte 5156fb7dd4 refactor(sdk): Move add_event_handler_impl to event_handler module 2022-08-05 16:16:41 +02:00
Jonas Platte 3b03ad804f refactor(sdk)!: Swap the async lock around event handlers for a sync one 2022-08-05 16:16:41 +02:00
Damir Jelić 603176f521 chore(crypto): Bump vodozemac
Vodozemac got some new knobs to twiddle with. This patch updates
vodozemac and sets the knobs to use the olm/megolm v1 supported
encryption schemes.
2022-08-05 14:06:42 +02:00
Jonas Platte dfec17e6af chore: Disable testing of crates without tests
This reduces the amount of "running 0 tests" spam when testing the whole
workspace and makes testing a little faster overall.
2022-08-05 11:10:31 +02:00
Kévin Commaille 7623f93bb3 fix(sdk)!: Make set_homeserver private
A user shouldn't need to change the homeserver after creating the client.
2022-08-05 09:55:55 +02:00
Jonas Platte a60620306c ci: Update tarpaulin.toml 2022-08-04 23:43:49 +02:00
Jonas Platte 1fea48359d ci: Checkout head ref instead of merge commit for coverage
… this *should* fix bogus coverage change reporting.
2022-08-04 23:43:49 +02:00
Jonas Platte 054dfa98a0 ci: Upgrade checkout action 2022-08-04 23:43:49 +02:00
Damir Jelić 593d4e6062 perf(crypto): Use an RwLock for the OutboundGroupSession 2022-08-04 17:36:03 +02:00
Jonas Platte 5a94ba7b80 refactor(sled)!: Align open_with_database with its documentation
It now takes a passphrase instead of a store cipher as the second argument.
2022-08-04 17:04:31 +02:00
Jonas Platte e1b7f3be05 refactor(sled): Simplify test code 2022-08-04 17:04:31 +02:00
Jonas Platte 6bed51f016 refactor(indexeddb)!: Use &str for name in public API 2022-08-04 17:04:31 +02:00
Jonas Platte 4db162b8a2 chore(indexeddb): Clean up Result usage 2022-08-04 17:04:31 +02:00
Jonas Platte 0b1bdd66f9 refactor(indexeddb): Export error types 2022-08-04 17:04:31 +02:00
Jonas Platte a4f3c3a070 refactor!: Give sled / indexeddb types unique names 2022-08-04 17:04:31 +02:00
Jonas Platte df7895d2c6 chore(indexeddb): Rename cryptostore => crypto_store
… for consistency with state_store.
2022-08-04 17:04:31 +02:00
Jonas Platte 694c36d741 chore(sled): Rename cryptostore => crypto_store
… for consistency with state_store.
2022-08-04 17:04:31 +02:00
Benjamin Kampmann b5329f99f1 Merge pull request #903 from gnunicorn/ben-getting-started-example
Improving examples
2022-08-04 15:41:26 +02:00
Damir Jelić f055e939e7 refactor(crypto): Use the vodozemac method to decide if a session is better
This patch delegates the decision making if a session is better to
vodozemac. Vodozemac has better insights if a group session can be
considered to be better.
2022-08-04 15:09:09 +02:00
Benjamin Kampmann c4c7c2bb23 docs: remove unused import and fix style of custom events example 2022-08-04 15:02:39 +02:00
Benjamin Kampmann efc0556124 doc: follow naming convention and use generated types from ruma 2022-08-04 13:31:16 +02:00
Benjamin Kampmann 9d588f7e00 doc: add example for sending and reacting on custom events 2022-08-04 13:13:48 +02:00
Benjamin Kampmann 323974fe4c Merge remote-tracking branch 'origin/main' into ben-getting-started-example 2022-08-04 12:22:42 +02:00
Benjamin Kampmann 3bdb2f22ea build(crypto-js): Pin yarg-parser to 21.0.1 to prevent upgrade bug
We are effected by https://github.com/yargs/yargs-parser/issues/452
through the transient dependency of jest on yargs
2022-08-04 12:21:18 +02:00
Damir Jelić ddf8577c84 refactor(crypto): Start using our own event types when we encrypt 2022-08-04 11:16:02 +02:00
Benjamin Kampmann 90c2dcdbc0 style: fix clippy lints 2022-08-04 11:02:28 +02:00
Benjamin Kampmann fe4bc0dc75 doc: fix docs of getting started bot 2022-08-03 17:07:59 +02:00
Benjamin Kampmann 69c8cdf304 Revert "fix: limit indexeddb features to target arch to stop clippy from complaining"
This reverts commit d54612b6e2.
2022-08-03 16:40:17 +02:00
Benjamin Kampmann daf92d7745 Merge remote-tracking branch 'origin/main' into ben-getting-started-example 2022-08-03 16:39:50 +02:00
Benjamin Kampmann ccbd9e5712 chore: rename Readme.md to README.md 2022-08-03 16:39:14 +02:00
Jonas Platte 2430d7f267 chore: Remove unneeded docsrs Cargo features 2022-08-03 15:33:53 +02:00
Damir Jelić 3ca3016539 refactor(crypto): Use the curve 25519 key type for inbound group sessions 2022-08-03 15:28:24 +02:00
Jonas Platte bbbd7942b0 fix(indexeddb): Export MigrationConflictStrategy 2022-08-03 15:17:05 +02:00
Jonas Platte 1a5953e01e chore(indexeddb): Appease clippy 2022-08-03 15:17:05 +02:00
Jonas Platte 3a19d8e5bf chore(indexeddb): Reduce target_arch feature gates
This allows smoother development as a regular 'cargo check' will
validate most of the code. It also makes rust-analyzer work for the
crate without any configuration.
2022-08-03 15:17:05 +02:00
Jonas Platte 0b7d0aa780 chore(common): Make timeout tests deterministic
… as well as simpler, and faster.
2022-08-03 14:54:43 +02:00
Jonas Platte 82f4e57a2c feat(sled): Print a clear error message when attempting to build on wasm 2022-08-03 14:48:45 +02:00
Jonas Platte 9714ce9edf refactor(sdk): Check permissible feature configuration in build script
… instead of through `compile_error!` invocations. This helps avoid
unhelpful errors from the unsupported feature configuration by aborting
compilation earlier.
2022-08-03 14:48:45 +02:00
Benjamin Kampmann 9538596fbb doc: Add getting-started example autojoin & command bot with plenty of source docs 2022-08-03 13:05:25 +02:00
Benjamin Kampmann 35be128139 docs: add Readme to examples root 2022-08-03 12:06:50 +02:00
Benjamin Kampmann d54612b6e2 fix: limit indexeddb features to target arch to stop clippy from complaining 2022-08-03 12:00:54 +02:00
Benjamin Kampmann a1bf53a331 Merge remote-tracking branch 'origin/main' into ben-getting-started-example 2022-08-03 11:39:50 +02:00
Kévin Commaille 9064e7b02d feat(sdk): Add support for refresh tokens 2022-08-03 10:42:28 +02:00
Damir Jelić ae261c2091 refactor(crypto): Refactor the key sharing tests a bit 2022-08-02 16:48:05 +02:00
Damir Jelić 968792ea00 refactor(crypto): Split out the forwarded room key accepting method 2022-08-02 16:48:05 +02:00
Benjamin Kampmann 42c88e840f Merge pull request #905 from gnunicorn/ben-fix-integration-test-coverage
ci: add backend server for integration test with tarpaulin
2022-08-02 16:14:26 +02:00
Benjamin Kampmann 38a71972e5 ci: add backend server for integration test with tarpaulin 2022-08-02 15:42:45 +02:00
Benjamin Kampmann d8caaed1ce docs: only document default workspace members, not all 2022-08-02 15:24:48 +02:00
Benjamin Kampmann 67e63c0d35 ci: update xtask, add ci to build examples 2022-08-02 15:13:27 +02:00
Benjamin Kampmann 4c7ddd7512 refactor: move examples from crates/matrix-sdk into separate crates in examples/ 2022-08-02 15:06:04 +02:00
Jonas Platte 9fca639f9b feat(sdk): Add room::Common::add_event_handler 2022-08-02 13:38:30 +02:00
Benjamin Kampmann 6cb87c64b5 Merge pull request #855 from gnunicorn/gnunicorn/issue833
Integration tests against an actual synapse server
2022-08-02 12:22:48 +02:00
Benjamin Kampmann a5875ff75d ci: update codecov config to exclude all testing crates 2022-08-02 10:59:18 +02:00
Benjamin Kampmann 9bb02f2419 fix: fix path to testing crate 2022-08-02 10:58:53 +02:00
Benjamin Kampmann 65be06ebad Merge remote-tracking branch 'origin/main' into gnunicorn/issue833 2022-08-02 10:32:09 +02:00
Damir Jelić 154538c4c9 fix(crypto): Make the secret receiving logic more obvious.
This patch ensures that the received secret has been sent by one of our
verified devices. We already ensure so for the secrets we import, since
we check that the cross signing keys match the public keys of our own
user idenity.

No other secrets get imported by this method but it could quite easily
become an issue if we start accepting more secret types.
2022-08-02 09:35:38 +02:00
Damir Jelić f23c16cf88 refactor(crypto): Split out the methods to receive a secret 2022-08-02 09:35:38 +02:00
Jonas Platte 165973121c fix(sdk): Make remove_event_handler work with room-specific handlers
As part of that, store only the ID, not the whole key in
EventHandlerWrapper, because the key is getting bigger with room_id.
2022-08-01 23:57:00 +02:00
Jonas Platte 31903d3cbc chore(sdk): Simplify remove_event_handler test 2022-08-01 23:55:44 +02:00
Jonas Platte 108f299e92 refactor(sdk): Inline handle_sync_events_wrapped_with into all callers
It was too generic to be readable.
2022-08-01 23:55:44 +02:00
Jonas Platte eb78815be5 refactor(sdk): Create non-generic fn call_event_handlers
Extracted out of handle_sync_events_wrapped_with.
2022-08-01 23:55:44 +02:00
Jonas Platte 0716f6afaa feat(sdk): Add Client::add_room_event_handler 2022-08-01 23:55:44 +02:00
Jonas Platte 78169d516e refactor(sdk): Avoid unnecessary double map lookup 2022-08-01 23:55:44 +02:00
Jonas Platte 061fe104a9 chore(sdk): Move EventHandler{Fut,Fn} into event_handler module 2022-08-01 23:55:43 +02:00
Benjamin Kampmann f91bdb28ba Merge pull request #897 from gnunicorn/ben-fix-documentation-generation
Fix documentation generation
2022-08-01 22:37:21 +02:00
Benjamin Kampmann 14429af511 Update .github/workflows/documentation.yml
Co-authored-by: Jonas Platte <jplatte+git@posteo.de>
2022-08-01 22:19:46 +02:00
Benjamin Kampmann 1e6e00b0c0 ci(integration-testing): split integration test off from regular tests 2022-08-01 18:54:33 +02:00
docweirdo 744bd8d0d2 feat(common): Add a generically usable timeout function 2022-08-01 18:23:30 +02:00
Benjamin Kampmann 06ad079099 Merge pull request #895 from matrix-org/ben-add-editoconfig
build: Adding .editorconfig
2022-08-01 15:23:58 +02:00
Benjamin Kampmann 7dfadd1848 ci(integration-testing): split integration test off from regular tests 2022-08-01 15:20:13 +02:00
Benjamin Kampmann 5089c1a7e9 ci(docs): force npm generated bindings if existing 2022-08-01 14:56:26 +02:00
Benjamin Kampmann c15a4bcdd4 build: Adding .editorconfig 2022-08-01 14:33:05 +02:00
Benjamin Kampmann 729836cf70 Merge pull request #894 from gnunicorn/ben-add-js-docs
ci(js) add npm docs of bindings to pages
2022-08-01 13:35:17 +02:00
Benjamin Kampmann 2d21c30639 Merge remote-tracking branch 'origin/main' into gnunicorn/issue833 2022-08-01 13:28:28 +02:00
Benjamin Kampmann 3842426788 ci: Remove unneeded github action params 2022-08-01 13:27:00 +02:00
Benjamin Kampmann c27016b2e7 testing: fix review grumbles 2022-08-01 13:01:50 +02:00
Benjamin Kampmann d0f58d0879 ci(js) add npm docs of bindings to pages 2022-08-01 12:17:36 +02:00
Kévin Commaille 4cdc91844e chore(ci): Fix new clippy warnings 2022-07-30 12:58:18 +02:00
Jonas Platte fe5d8fb40e refactor: Deprecate ClientBuilder::{crypto_store, state_store} 2022-07-29 17:22:12 +02:00
Jonas Platte ffb4c6d6ef docs: Use new ClientBuilder methods in examples 2022-07-29 17:22:12 +02:00
Jonas Platte 93c4c5d128 feat(sdk): Add ClientBuilder::indexeddb_store 2022-07-29 17:22:12 +02:00
Jonas Platte ec3a6e4b15 feat(sdk): Add ClientBuilder::sled_store 2022-07-29 17:22:12 +02:00
Jonas Platte ecbcf734b9 docs(sled): Clean up make_store_config description 2022-07-29 17:22:12 +02:00
Jonas Platte 75f08aed69 docs(sdk): Fix outdated description of ClientBuilder::store_config 2022-07-29 17:22:12 +02:00
Jonas Platte 255955555d refactor(sdk): Add back register_event_handler[_context] as deprecated methods
… using their original signatures so upgrading matrix-sdk is easier.
2022-07-29 14:39:28 +02:00
Jonas Platte 27b858308c refactor(sdk)!: Rename {register => add}_event_handler[_context]
It's shorter and better fits with the new remove_event_handler method.
2022-07-29 14:39:28 +02:00
Jonas Platte a21ac5d6e4 Disable debug-info for the dev profile 2022-07-29 11:28:47 +02:00
Damir Jelić 4db041ce9a docs(crypto): Improve the wording on the recipient collection method
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2022-07-29 10:20:36 +02:00
Damir Jelić c194e08942 fix(crypto): Rotate the outbound group session if the algorithm changes 2022-07-29 10:20:36 +02:00
Damir Jelić 713e96d3a8 fix(crypto): Store intermediate changes to the outbound group session
This fixes a bug where we would lose already generated to-device
requests carrying an m.room_key when the client gets restarted.

This only happens if the request was generated after the initial
share of the room key. Such requests get generated if a new device
or member join the group.
2022-07-29 10:01:58 +02:00
Jonas Platte b36f79a1bb doc: Make rustfmt format code blocks in documentation 2022-07-28 20:06:12 +02:00
Jonas Platte 504464c1e8 chore: Remove edition from .rustfmt.toml
It will be picked up from Cargo.toml.
2022-07-28 20:06:12 +02:00
Doug b884c5baae chore(bindings/ffi): Print each step when building XCFramework. 2022-07-28 13:37:02 +02:00
Doug c15fb5e5b7 fix(sdk): Reduce retry length on homeserver discovery 2022-07-28 13:37:02 +02:00
Damir Jelić 0555216f37 chore(crypto): Introduce a helper function to convert events in tests 2022-07-28 12:48:12 +02:00
Damir Jelić 03a4814a1a refactor(crypto): Use our own type for megolm 2022-07-28 12:48:12 +02:00
Damir Jelić 80c7f057cc refactor(crypto): Use our own struct for encrypted to-device events 2022-07-28 12:48:12 +02:00
Damir Jelić 7e70997e1c feat(crypto): Add customized m.room.encrypted events 2022-07-28 12:48:12 +02:00
Damir Jelić 2bb7914611 refactor(crypto): Make the event type an associated constant 2022-07-28 12:48:12 +02:00
Benjamin Kampmann c8c793da98 Merge pull request #768 from gnunicorn/gnunicorn/issue756
Bump db version, "upgrade" path to new db in sled and indexeddb
2022-07-28 09:51:27 +02:00
Damir Jelić e501979d92 feat(bindings/ffi): Allow setting the timeline limit for the sync setup 2022-07-27 19:23:10 +02:00
Doug 57c1e68893 Rename limit to timeline_limit.
Use a u16 instead.
2022-07-27 18:03:04 +01:00
Doug a875c4b373 Expose room membership in the FFI. 2022-07-27 17:23:09 +01:00
Doug f6e215dac3 Add limit parameter to start_sync. 2022-07-27 17:22:17 +01:00
Jonas Platte 4415ed9b58 refactor: Improve tracing events
* Use fields more
* Adjust some wording
2022-07-27 17:47:01 +02:00
Benjamin Kampmann 59fc81b957 ci(xtask): bump wasm timeout to make the ci pass 2022-07-27 16:58:20 +02:00
Benjamin Kampmann 770ac193a7 fix(indexeddb): import errors 2022-07-27 15:08:51 +02:00
Benjamin Kampmann fe40b3753a Merge pull request #870 from Hywan/feat-crypto-js-npm-publish
chore: Update or add `git-cliff` config' & prepare `crypto-js` for publishing
2022-07-27 14:28:40 +02:00
Benjamin Kampmann e871114436 docs(crypto-ffi): fixing path 2022-07-27 13:43:39 +02:00
Benjamin Kampmann fb4dd4d875 test(crypto-js): fixing path 2022-07-27 13:32:26 +02:00
Benjamin Kampmann 5806b3fbe9 ci(crypto-js): use a specific node version to run tests 2022-07-27 13:23:44 +02:00
Benjamin Kampmann e1be222558 style: fixing fmt 2022-07-27 13:09:08 +02:00
Benjamin Kampmann 37f0926832 chore: prefer to user Store::builder 2022-07-27 12:59:53 +02:00
Benjamin Kampmann 936731065b fix(indexeddb): specify futures import 2022-07-27 12:53:54 +02:00
Benjamin Kampmann 088699b6a4 docs(xtask): remove comment 2022-07-27 12:20:46 +02:00
Benjamin Kampmann b2da8b7dd7 fix(examples): update to renaming 2022-07-27 12:12:17 +02:00
Benjamin Kampmann b80d3c2f2d Merge remote-tracking branch 'origin/main' into gnunicorn/issue756 2022-07-27 12:04:56 +02:00
Benjamin Kampmann eb1dfd9690 docs(indexeddb): document builder functions 2022-07-27 11:52:13 +02:00
Benjamin Kampmann 54c181a9ad chore: cleaning up builder usage in sled and indexeddb 2022-07-27 11:51:56 +02:00
Benjamin Kampmann 003c946581 chore(sled): ascending order for dependencies 2022-07-27 10:59:02 +02:00
docweirdo 82731a5e89 feat(sdk): Add support for removing event handlers 2022-07-27 05:09:22 +00:00
Damir Jelić 4175e6badf feat(bindings/ffi): Allow clients to be restored using only an access token
This functionality is important to allow clients to log in using an OIDC server. The OIDC server returns only an access token, the client needs to fetch the user ID and device ID from the Matrix server separately.

This lives in the bindings for now since OIDC support in Matrix is being actively developed.
2022-07-26 17:10:50 +02:00
Benjamin Kampmann 008c0f4659 Merge pull request #860 from matrix-org/doug/client-path
Use a consistent store path when logging in through the FFI.
2022-07-26 16:33:32 +02:00
Doug a951a273e7 Rebase and rename method.
Add a device ID parameter to restore_with_access_token
2022-07-26 15:00:02 +01:00
Doug f13f590b7f Add login_access_token to the FFI auth service. 2022-07-26 15:00:02 +01:00
Doug 1eadd0ba2d Revert changes to store path. Use whoami for path. 2022-07-26 14:50:51 +01:00
Doug 3b8a2ca2d7 Add missing path method to IndexeddbStore. 2022-07-26 14:50:51 +01:00
Doug b6b1f904e0 Fix clippy warning. 2022-07-26 14:50:51 +01:00
Doug f31528c926 Allow a client to be built without a store path.
Throw an error on login if it is missing.
2022-07-26 14:50:51 +01:00
Doug 05fab8c394 Add a store_path method on the FFI client. 2022-07-26 14:48:37 +01:00
Jonas Platte e37e62c92c chore(sdk): Fix event handler test
It wasn't actually testing typing and power-levels event handlers before.
2022-07-26 11:11:10 +02:00
Jonas Platte d162dd07d5 refactor(sdk): Split SyncEvent::ID into KIND and TYPE
This should make things slightly easier to read.
2022-07-25 20:33:43 +02:00
Jonas Platte 42d3ebbe87 chore: Remove ugly commas right before closing parentheses 2022-07-25 20:30:13 +02:00
Jonas Platte 4e7bd6760a chore: Replace qualified uses of tracing macros with imports 2022-07-25 20:30:13 +02:00
Jonas Platte ab9476960b chore(sled): Fix line length overflow 2022-07-25 20:30:13 +02:00
Jonas Platte b3092bd1d9 chore(indexeddb): Remove unnecessary .to_string() call in test code 2022-07-25 20:30:13 +02:00
Jonas Platte c2ad4b7d82 fix(appservice): Respond with a non-stringified empty object
warp::reply::json() serializes its argument to JSON, passing it an
already-serialized JSON string doesn't do the right thing.
2022-07-25 20:30:13 +02:00
Jonas Platte f8e729f7f3 chore: Use implicit named arguments for formatting macros more 2022-07-25 20:30:12 +02:00
Jonas Platte 5c88bd1595 refactor(sdk): Remove ID from EventHandler
Turns out it wasn't actually needed.
2022-07-25 20:30:12 +02:00
Damir Jelić 204441f2b3 Fix a formatting issue in a doc example
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2022-07-25 19:06:15 +02:00
Damir Jelić 79d13148fb chore: Remove TryFrom/TryInto imports 2022-07-25 19:06:15 +02:00
Julian Sparber c247de95b6 client: Remove redudant delay after sync error
If the last sync was less then a 1s ago we wait for a 1s, it doesn't
make sense to wait an additional second on an error. Also the stream
sync api returns the error after a delay of 1s, which doesn't make
sense.
2022-07-25 12:01:28 +02:00
Johannes Becker 6d87dd8011 refactor(appservice)!: Make USER_MEMBER const private" 2022-07-22 09:33:42 +02:00
Jonas Platte 4b6a28da0d chore(sdk): Fix line length overflow in common.rs 2022-07-21 19:24:42 +02:00
Benjamin Kampmann bf6e1b594d fix(integration-tests): requires multi-threading feature from tokio 2022-07-21 18:06:48 +02:00
Benjamin Kampmann 59332e46b5 switch to multithreaded tokio test runner 2022-07-21 17:58:50 +02:00
Benjamin Kampmann c070b96a68 Merge remote-tracking branch 'origin/main' into gnunicorn/issue833 2022-07-21 17:37:49 +02:00
Benjamin Kampmann d68d6ead69 chore: let's check the logs 2022-07-21 17:31:58 +02:00
Benjamin Kampmann f34c2f0574 ci: fix typos 2022-07-21 17:28:38 +02:00
Benjamin Kampmann aad167b792 style(indexeddb): fixing rust fmt 2022-07-21 17:25:43 +02:00
Benjamin Kampmann 814a30064f fix(sled-inspector): new builder fn 2022-07-21 17:18:43 +02:00
Ivan Enderlin 6ce5d0b507 chore(crypto-js): Add cliff.toml for git-cliff. 2022-07-21 15:50:11 +02:00
Ivan Enderlin db7824efbd chore(crypto-nodejs): Update Conventional Commits types. 2022-07-21 15:49:26 +02:00
Ivan Enderlin ee4702d04a feat(bindings/crypto-js) Update package name, and use package scope. 2022-07-21 15:47:55 +02:00
Ivan Enderlin d68b6ea64d doc: Fix formatting. 2022-07-21 15:47:00 +02:00
Ivan Enderlin 49bc950fdd doc: Add missing scope for matrix-sdk-crypto. 2022-07-21 15:46:21 +02:00
Ivan Enderlin 7001113877 doc: Propose conventional commits scopes & types
doc: Propose conventional commits scopes & types
2022-07-21 15:44:43 +02:00
Ivan Enderlin c0d3099e2e doc: Propose conventional commits types. 2022-07-21 15:43:31 +02:00
Ivan Enderlin 49e5d73d83 doc: Propose conventional commits scopes. 2022-07-21 13:59:31 +02:00
Benjamin Kampmann 2ffaaeeae2 docs(sled): fixing examples and utils 2022-07-21 13:18:15 +02:00
Benjamin Kampmann 60eef8967f fix(sled-store): apply review requests 2022-07-21 11:52:04 +02:00
Benjamin Kampmann ed893ed5b0 Merge remote-tracking branch 'origin/main' into gnunicorn/issue756 2022-07-21 11:38:10 +02:00
Jonas Platte 4e2bd14a27 chore: Silence buggy clippy lint 2022-07-21 11:30:13 +02:00
Jonas Platte 0b3b757120 refactor: Make Store type internal to base crate 2022-07-21 11:30:13 +02:00
Jonas Platte 63d01dd20e refactor(base): Stop using Store type in store integration tests 2022-07-21 11:30:13 +02:00
Jonas Platte a43005dec2 refactor(base): Add RoomInfo::new constructor 2022-07-21 11:30:13 +02:00
Jonas Platte 4187aa400f refactor: Remove Store type usage from sled-state-inspector
Requires using more explicit syntax for some calls because SledStore has
methods of the same names with different (harder to use) signatures.
2022-07-21 11:30:13 +02:00
Jonas Platte cf8f3bf7cc refactor(sdk)!: Update MessageOptions API once again
… to match the latest changes in Ruma.
2022-07-21 11:18:09 +02:00
Jonas Platte 5b66bed1f0 chore: Remove unused imports from example 2022-07-21 11:18:09 +02:00
Benjamin Kampmann 1668c173e6 Merge pull request #836 from matrix-org/ben-release-crypto-nodes-beta0
Release crypto-nodejs beta1
2022-07-21 10:59:46 +02:00
Jonas Platte 76fd9bd963 fix(sdk): Call to-device handlers 2022-07-21 10:30:13 +02:00
Benjamin Kampmann 6f18e35b72 Merge remote-tracking branch 'origin/main' into ben-release-crypto-nodes-beta0 2022-07-21 10:25:26 +02:00
Johannes Becker 5f7e91c2e7 refactor(appservice)!: Cleanup 2022-07-21 09:01:53 +02:00
Jonas Platte 45829efa7e feat(bindings): Tracing configuration through FFI 2022-07-20 16:38:51 +02:00
Kévin Commaille a6bd7fc82f chore: Update Ruma 2022-07-20 12:18:06 +00:00
Stefan Ceriu 5c53a5f699 chore(bindings/apple): Remove unnecessary +nightly flag from debug builds 2022-07-20 14:53:00 +03:00
Stefan Ceriu 021bf55074 feat(bindings/sdk-ffi): Expose full tracing configuration string through ffi. 2022-07-20 13:39:15 +03:00
Stefan Ceriu 0043de4028 Update bindings/matrix-sdk-ffi/Cargo.toml
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2022-07-20 13:28:58 +03:00
Stefan Ceriu f9bb86c52f feat(bindings/sdk-ffi): Allow ffi users to configure tracing and log levels 2022-07-20 12:56:42 +03:00
Stefan Ceriu 1cd18f49aa chore(bindings/apple): Remove now unnecessary debug script module import following module map rename 2022-07-20 12:29:36 +03:00
Benjamin Kampmann 8b160dfd38 Merge pull request #857 from matrix-org/doug/homeserver-details
Add a `HomeserverLoginDetails` to the FFI's auth service.
2022-07-20 10:27:09 +02:00
Doug 77f7dbbbc8 Support server URLs. Join homeserver details futures. 2022-07-19 16:16:03 +01:00
Ivan Enderlin fe590735c1 fix(bindings/crypto-nodejs): Fix CI typo
fix(bindings/crypto-nodejs): Rrrrr
2022-07-19 16:43:40 +02:00
Ivan Enderlin 0360b13cdb fix(bindings/crypto-nodejs): Rrrrr 2022-07-19 16:12:43 +02:00
Jonas Platte f30419446f refactor(sdk)!: Make from in MessageOptions optional 2022-07-19 15:25:16 +02:00
Doug 93d879f356 Add homeserver_details property. 2022-07-19 12:48:00 +01:00
Benjamin Kampmann c20c23bc19 Merge pull request #856 from matrix-org/fixing-clippy-lint
style(crypto): use matches for legibility
2022-07-19 12:54:44 +02:00
Johannes Becker a174d0f669 sdk: Make from in MessageOptions optional 2022-07-19 12:46:48 +02:00
Doug a10a26a68d Tidy up authentication service.
Add HomeserverLoginDetails
2022-07-19 11:36:34 +01:00
Benjamin Kampmann 1940ebefcb style(crypto): use matches for legibility 2022-07-19 12:22:53 +02:00
Benjamin Kampmann b91217f53b Merge remote-tracking branch 'origin/main' into gnunicorn/issue833 2022-07-19 12:19:20 +02:00
Benjamin Kampmann 3d3db96734 style(integration-tests): minor rust fmt fixes 2022-07-19 12:11:13 +02:00
Benjamin Kampmann b24f7b7ad7 ci(integration-tests): switch to non-docker github action 2022-07-19 12:05:49 +02:00
Benjamin Kampmann 9502d32941 Merge remote-tracking branch 'origin/main' into gnunicorn/issue833 2022-07-19 12:03:47 +02:00
Benjamin Kampmann 9318c2f1e8 ci(crypto-nodejs): switch to main branch 2022-07-19 11:36:30 +02:00
Benjamin Kampmann 2d03cb5c79 Merge pull request #852 from zecakeh/test-bulk-member
test(sdk): Split tests for permalinks
2022-07-19 11:34:48 +02:00
Johannes Becker aa8206d6c8 chore: Bump ruma 2022-07-18 19:32:16 +02:00
Johannes Becker f937d82336 chore: Bump ruma 2022-07-18 16:46:34 +00:00
Benjamin Kampmann 3f71818704 ci(test): switch from docker to compose 2022-07-18 18:10:41 +02:00
Benjamin Kampmann 025db83af3 ci(test): cancel previous calls and fix the docker logs cmd 2022-07-18 18:06:54 +02:00
Benjamin Kampmann 8520976417 style(test): fix styles of integration tests 2022-07-18 17:55:31 +02:00
Benjamin Kampmann 06cab75df3 ci(sdk): Add integration tests to CI 2022-07-18 17:39:51 +02:00
Benjamin Kampmann 47a8c62f44 fix(sdk): fetch invitiation details without sync 2022-07-18 17:39:29 +02:00
Benjamin Kampmann e2f1f01cb2 test(sdk): creating integration tests for invitations 2022-07-18 17:33:09 +02:00
Ivan Enderlin fdef2dd86d feat(bindings/crypto-js): Redirect panics and logs into JavaScript console
feat(bindings/crypto-js): Redirect panics and logs into JavaScript console
2022-07-18 14:59:50 +02:00
Ivan Enderlin c72ec36b3a chore(style) Make cargo fmt happy. 2022-07-18 14:39:39 +02:00
Benjamin Kampmann e3febd6f1f refactore(test): move testing out of regular build environment 2022-07-18 14:37:51 +02:00
Kévin Commaille b98e3d80a0 test(sdk): Split tests for permalinks 2022-07-18 13:39:37 +02:00
Kévin Commaille c0a7b17324 test: Add method to create room member events in bulk 2022-07-18 13:39:37 +02:00
Kévin Commaille 66c5d5311e test: Add methods to add events in bulk 2022-07-18 13:39:36 +02:00
Stefan Ceriu f215c92d0b fix(bindings/apple): Remove briding header as no longer needed after corectly naming the module map 2022-07-18 13:09:39 +02:00
Stefan Ceriu d571ca718d fix(bindings/apple): Allow any platform OS simulator to run the Apple specific unit tests. 2022-07-18 13:09:39 +02:00
Kévin Commaille 37eb058dac test: Reorganize JSON responses and events 2022-07-18 10:46:45 +02:00
Kévin Commaille 45ecd89387 test: Build sync response per room and event type
Allow to have more customizable and complete responses
2022-07-18 10:46:45 +02:00
Kévin Commaille 5d916e4a67 test: Expose default room ID used in sync JSON responses 2022-07-18 10:46:45 +02:00
Ivan Enderlin daa0fc0206 doc(bindings/crypto-js): Fix typos. 2022-07-18 10:05:05 +02:00
Ivan Enderlin 3d1c96fbec feat(bindings/crypto-js): Redirect errors to console.error. 2022-07-18 09:51:44 +02:00
Ivan Enderlin decd3fcb43 feat(bindings/crypto-js) Simplify code for feature = "tracing".
This patch creates one `inner` module for when `feature = "tracing"`,
and one for when `no(feature = "tracing")`. Then, let's expose
everything from `inner::*`.

This patch also replaces `Tracing.install` by `new Tracing`. In case
of `not(feature = "tracing")`, `new Tracing` raises an error.

The goal is to remove all the `#[cfg(…)]` annotations everywhere. Now
there is only 2 of them.
2022-07-18 09:44:42 +02:00
Ivan Enderlin c763ce3f41 feat(bindings/crypto-js): Tracing can be installed more than once. 2022-07-18 09:23:37 +02:00
Johannes Becker 09c56ea057 feat(appservice)!: Allow specifying device id for registration 2022-07-14 15:23:21 +02:00
github-actions 163ce94806 Tagging Crypto-Node.js for release 2022-07-14 10:40:57 +00:00
Benjamin Kampmann ebfa235dab chore(crypto-nodejs): Update changelog for beta.1 2022-07-14 12:31:04 +02:00
Benjamin Kampmann 0112396c99 ci(crypto-nodejs): trigger build for new tag 2022-07-14 12:31:04 +02:00
Benjamin Kampmann 8170d2c996 Merge remote-tracking branch 'origin/main' into ben-release-crypto-nodes-beta-1 2022-07-14 11:40:45 +02:00
Benjamin Kampmann 41de3e0af8 Merge pull request #844 from Hywan/fix-issue-842
fix(bindings/crypto-nodejs): Fix pre-built download link
2022-07-14 11:36:15 +02:00
Benjamin Kampmann 81bf300000 Merge pull request #839 from johannescpk/appservice/virtual-users
feat(appservice): Add method to get virtual user map
2022-07-14 11:31:02 +02:00
Benjamin Kampmann 594e8c04cd Merge pull request #840 from matrix-org/jplatte/optional-dep-features
Remove implicit features for optional dependencies
2022-07-14 11:30:03 +02:00
Benjamin Kampmann b6d94ab7c6 Merge pull request #845 from Hywan/fix-issue-843
fix(bindings/crypto-js): Use `cross-env` to pass envvar on Windows
2022-07-14 11:28:57 +02:00
Ivan Enderlin 283c5ff51e fix(bindings/crypto-js): Let's not deal with Console.group.
Events and spans from `tracing` can happen asynchronously, and could
mess the `Console.group` structure.
2022-07-14 09:04:44 +02:00
Ivan Enderlin bb631f2f79 feat(bindings/crypto-js): Add ability to turn Tracing on and off, and change logger min level.
The patch updates the code to use `tracing_subscriber::reload`, so
that we get a `Handle` that can be used to modify the tracing at
runtime.

This patch also adds a new `tracing` feature.

This patch finally adds a test suite for the `Tracing` API.
2022-07-14 09:04:13 +02:00
Ivan Enderlin d39baf1295 test(bindings/crypto-js): Encrypt and decrypt a valid message. 2022-07-14 09:04:13 +02:00
Ivan Enderlin f5016dbb97 feat(bindings/crypto-js): Define Layer.max_level_hint. 2022-07-14 09:04:13 +02:00
Ivan Enderlin e7d0ee4379 !fixup 2022-07-14 09:04:13 +02:00
Ivan Enderlin 70561f9649 feat(bindings/crypto-js): Add the userLogger function to enable logging into Console. 2022-07-14 09:04:13 +02:00
Ivan Enderlin 8f8bd40e8d feat(bindings/crypto-js): Redirect Rust panics to JavaScript console. 2022-07-14 09:04:13 +02:00
Ivan Enderlin 04d326eec1 doc(bindings/crypto-nodejs): Fix package name in the README.md
Fix package name in readme for nodejs bindings
2022-07-14 08:39:53 +02:00
Ivan Enderlin 3567d19359 fix(bindings/crypto-js): Use cross-env to pass envvar on Windows. 2022-07-14 08:26:57 +02:00
Ivan Enderlin 65b1dfef6f fix(bindings/crypto-nodejs): Fix pre-built download link. 2022-07-14 08:14:45 +02:00
Travis Ralston 4f1718e587 Fix package name in readme for nodejs bindings 2022-07-13 13:45:07 -06:00
Jonas Platte 3c5f30d41e refactor: Remove implicit features for optional dependencies
Consistently use `dep:` syntax for optional dependencies so they don't
implicitly act as features of their own.
2022-07-13 18:55:41 +02:00
Jonas Platte 529bdc8e0a refactor: Remove unused optional dependencies 2022-07-13 18:23:52 +02:00
Johannes Becker f55a86dd66 feat(appservice): Add method to get virtual user map 2022-07-13 17:16:12 +02:00
Benjamin Kampmann f87764fabb ci(ffi-apple): fixing ffi build for apple
Merge pull request #806 from matrix-org/stefan/ffi-workflow-optimization
2022-07-13 11:58:26 +02:00
Stefan Ceriu 65654de7eb chore: sdk-ffi apple - try building the framework on x86_64 outside of Xcode 2022-07-13 11:19:07 +02:00
Stefan Ceriu 399bbc25e9 chore: sdk-ffi apple - run the CI build script from the Xcode project and only for the active architecture 2022-07-13 11:19:06 +02:00
Stefan Ceriu c61dbb657e chore: sdk-ffi apple - drop sample project deployment target to iOS 15 and macOS 12, disable catalyst. 2022-07-13 11:19:06 +02:00
Stefan Ceriu a73b104c59 chore: sdk-ffi apple - rename modulemap to module.modulemap as per xcframework specifications 2022-07-13 11:19:06 +02:00
Stefan Ceriu c10961f068 chore: sdk-ffi apple - remove mac catalyst target support 2022-07-13 11:19:06 +02:00
Benjamin Kampmann 15e22cba47 Merge pull request #837 from johannescpk/appservice/refactor-cleanup
refactor(appservice)!: Improve API and cleanup docs
2022-07-13 11:16:47 +02:00
Johannes Becker ec00af0bca refactor(appservice)!: Improve API and cleanup docs 2022-07-13 10:11:43 +02:00
Benjamin Kampmann a4f1c404f6 ci(crypto-nodejs): set npm publish level to public
Release Crypto-Node.js / Upload prebuilt libraries (gcc-aarch64-linux-gnu g++-aarch64-linux-gnu, ubuntu-latest, aarch64-unknown-linux-gnu) (push) Failing after 45s
Release Crypto-Node.js / Upload prebuilt libraries (gcc-arm-linux-gnueabihf, ubuntu-latest, arm-unknown-linux-gnueabihf) (push) Failing after 41s
Release Crypto-Node.js / Upload prebuilt libraries (gcc-i686-linux-gnu g++-i686-linux-gnu, ubuntu-latest, i686-unknown-linux-gnu) (push) Failing after 30s
Release Crypto-Node.js / Upload prebuilt libraries (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Failing after 42s
Release Crypto-Node.js / Upload prebuilt libraries (ubuntu-latest, x86_64-unknown-linux-musl) (push) Failing after 35s
Release Crypto-Node.js / Upload prebuilt libraries (macos-latest, aarch64-apple-darwin) (push) Has been cancelled
Release Crypto-Node.js / Upload prebuilt libraries (macos-latest, x86_64-apple-darwin) (push) Has been cancelled
Release Crypto-Node.js / Upload prebuilt libraries (windows-latest, aarch64-pc-windows-msvc) (push) Has been cancelled
Release Crypto-Node.js / Upload prebuilt libraries (windows-latest, i686-pc-windows-msvc) (push) Has been cancelled
Release Crypto-Node.js / Upload prebuilt libraries (windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
Release Crypto-Node.js / Package nodejs package (push) Has been cancelled
2022-07-12 19:28:59 +02:00
Benjamin Kampmann 402d061d42 ci(crypto-nodejs): fixing path to npm package for publishing 2022-07-12 18:02:59 +02:00
Benjamin Kampmann 2a1bc372fc chore(crypto-nodejs): setting version 2022-07-12 17:35:46 +02:00
Benjamin Kampmann bcab2a6d8c ci(crypto-nodejs): setting base branch 2022-07-12 17:24:34 +02:00
Benjamin Kampmann 703b3a3561 ci(crypto-nodejs): Fixing typo in workflow 2022-07-12 17:18:54 +02:00
Benjamin Kampmann 36f62ce67f ci(crypto-nodejs): Use regular npm version to set version 2022-07-12 17:14:15 +02:00
Benjamin Kampmann 9bc605a76d ci(crypto-nodejs): FIxing name and directory for action 2022-07-12 17:11:20 +02:00
Benjamin Kampmann 13a6825af7 ci(crypto-nodejs): Fix workflow syntax error 2022-07-12 17:07:49 +02:00
Benjamin Kampmann c5796991e8 chore(crypto-nodejs): Adding changelog 2022-07-12 17:05:40 +02:00
Benjamin Kampmann 9a45325683 ci(crypto-nodejs): use org-wide secrets (#835) 2022-07-12 17:00:44 +02:00
Ivan Enderlin 0bde5ccf38 feat(bindings/crypto-nodejs): Add #[napi(strict)] to force type checking from JavaScript (#829)
* feat(bindings/crypto-nodejs): Add `#[napi(strict)]` to force type checking from JavaScript.

* chore(bindings/crypto-nodejs): Use our own fork of `napi-rs` for the moment.
2022-07-12 16:24:24 +02:00
Benjamin Kampmann 94b635c074 build(crypto-nodejs): Crypto Node.js release infrastructure (#763)
* feat(crypto-nodejs): Download lib binary in postinstall

* build(crypto-nodejs): Workflow to prebuild napi bindings

* ci(crypto-nodejs): Disable broken target, install without download

* ci(apple-ffi): Don't run for drafts

* ci(coverage): Don't run for draft PRs

* fix(crypto-nodejs): bind to current version for download

* fix(crypto-nodejs): Ignore libs and package

* ci(crypto-nodejs): Build and upload NPM package

* fix(crypto-nodejs): Set proper target list

* ci(crypto-nodejs): Remove FreeBSD from build pipeline

* ci(crypto-nodejs): Linkers for linux cross compile

* ci(crypto-nodejs): Add arm64 build for windows

* ci(crypto-nodejs): Proper linkers for arm and musl

* ci(crypto-nodejs): Correct apt command for musl

* fix(crypto-nodejs): Drop arm64 linux musl support

* ci(crypto-nodejs): Manual Workflow trigger process

* chore(crypto-nodejs): Get Github to pickup our action

* ci(crypto-nodejs): Add i686 Linux built

* ci(crypto-nodejs): Configure cliff for nodejs changelogs

* ci(crypto-nodejs): Proper gcc for i868 targets

* docs(crypto-nodejs): Add supported targets for npm install

* ci(crypto-nodejs): Limit building of binaries to tags

* style: consol.log -> console.info; Improve docs

Co-authored-by: Ivan Enderlin <ivan@mnt.io>

* activate for testing

* fix broken merge

* 0.1.0

* fix(js): put in the proper package name

* activate for PR for testing

* fix(nodejs): getting ready for publishing

* ci(crypto-nodejs): Adding docs and fixing naming for workflows

* typo: missed one

* fixing package name

Co-authored-by: Ivan Enderlin <ivan@mnt.io>
2022-07-12 16:05:57 +02:00
Benjamin Kampmann f69123b0d8 style(indexeddb): rust fmt 2022-07-12 13:11:50 +02:00
Benjamin Kampmann 15be2dc45e Merge pull request #832 from johannescpk/sdk/identity-assertion-session
fix(sdk): Can't assert identity without session
2022-07-12 12:25:46 +02:00
Johannes Becker 420ca26bf5 fix(sdk): Can't assert identity without session 2022-07-12 10:44:56 +02:00
Benjamin Kampmann c1f0c73728 fix(indexeddb): New migration didn't create schema for new db 2022-07-11 16:18:29 +02:00
Benjamin Kampmann 54ed1af223 Merge remote-tracking branch 'origin/main' into gnunicorn/issue756 2022-07-11 15:17:03 +02:00
Benjamin Kampmann 6a57461f74 feat(indexeddb)!: Implement StateStoreBuilder pattern to configure migration preferences 2022-07-11 14:28:09 +02:00
Kévin Commaille 2d0653894c refactor(test): Rename LOGOUT to EMPTY
This name is more accurate for its uses.
2022-07-09 17:59:45 +02:00
Damir Jelić 47cfac7f4c test: Optimize sha2 even in debug builds
This makes the tests finish on my machine twice as fast. This works
mainly because some tests utilize pbkdf2 to derive a key from a
passphrase.
2022-07-08 18:47:36 +02:00
Kévin Commaille 9539cbcfb9 test(appservice): Replace mockito with wiremock 2022-07-08 16:33:29 +02:00
Kévin Commaille 9778518347 test(sdk): Replace mockito with wiremock 2022-07-08 16:33:29 +02:00
Ivan Enderlin dc2276cd8a feat(bindings/crypto-nodejs): Implement an Attachment API.
feat(bindings/crypto-nodejs): Implement an `Attachment` API.
2022-07-08 16:26:19 +02:00
Damir Jelić f1c880ff5f feat(bindings/ffi): Add an authentication service
This adds a basic authentication service to the bindings that abstracts away the Client until a login has been completed successfully.
2022-07-08 12:24:15 +02:00
Damir Jelić 93e5728d65 test(sdk): Move the integration tests
This moves the bulk of the Client tests into integration tests.
2022-07-08 12:08:27 +02:00
Damir Jelić a7af96d081 feat(crypto): Customized event types
This patch adds customized event types, currently only for the
m.room_key and m.secret.send to-device events.

This allows us to:
    a) Deserialize the session_key field into a vodozemac type
    b) Control when we zeroize secrets better
2022-07-07 19:20:42 +02:00
Benjamin Kampmann d7b974ac04 build(xtask): indexeddb alias for ci wasm-commands 2022-07-07 18:35:54 +02:00
Benjamin Kampmann aae9b5c6d8 refactor(test)!: More debugging info on errors in async_test macro 2022-07-07 18:35:16 +02:00
Doug 0dee880cd0 Address PR comments. 2022-07-07 17:15:12 +01:00
Ivan Enderlin f0190b4601 feat(bindings/crypto-nodejs): Transform timeout into milliseconds
feat(bindings/crypto-nodejs): Transform `timeout` into milliseconds
2022-07-07 14:54:18 +02:00
Ivan Enderlin 6d83f01e73 fix(sdk): THe MediaEncryptionInfo.web_key has been renamed. 2022-07-07 13:59:36 +02:00
Ivan Enderlin 2eb5fc77f5 feat(bindings/crypto-nodejs): Remove Clone impl for MediaEncryptionInfo.
We don't want to clone a struct that contains a secret.

However, on the Node.js side, we can only receive arguments by
references. The problem we have is that we cannot transfer the
ownership of `MediaEncryptionInfo` to `AttachmentDecryptor` because we
don't own it. To simulate this behavior, we use `Option.take`.

A new method then appears:
`EncryptedAttachment.hasMediaEncryptionInfoBeenConsumed` to know if
the media encryption info has been consumed by `Attachment.decrypt`
already or not. That way, we can decrypt only once. It is possible to
do a JSON-encoded backup of the media encryption info by calling
`EncryptedAttachment.mediaEncryptionInfo` though.
2022-07-07 13:53:46 +02:00
Kévin Commaille 5ab8bd0885 Fix missing import 2022-07-07 13:24:54 +02:00
Kévin Commaille ee69863912 Move event permalink test 2022-07-07 13:19:31 +02:00
Kévin Commaille e87d599f84 Merge remote-tracking branch 'upstream/main' into integration-tests 2022-07-07 13:16:09 +02:00
Ivan Enderlin 0b011d9097 doc(bindings/crypto-nodejs): Add link to the specification. 2022-07-07 13:15:14 +02:00
Ivan Enderlin 0f5851cc01 chore(crypto): Rename MediaEncryptionInfo.web_key to .key. 2022-07-07 13:14:05 +02:00
Kévin Commaille d6a2f15c68 Simplify use of via
Due to a ruma upgrade
2022-07-07 12:30:15 +02:00
Kévin Commaille 36a47c28ed Add note that the event should be part of the room 2022-07-07 12:30:15 +02:00
Kévin Commaille 900016b249 feat(sdk): Get a permalink for an event 2022-07-07 12:30:15 +02:00
Kévin Commaille de60a24602 Remove __test feature 2022-07-07 11:26:49 +02:00
Ivan Enderlin 29c10b8424 feat(bindings/crypto-nodejs): Convert timeout from u128 to u64.
First, u128 has a bug in `serde`,
cf. https://github.com/serde-rs/json/issues/625.

Second, we don't need to represent the timeout as a u128, it's clearly
too large. This patch tries to convert it to u64. It should never
fail, but we propagate the error anyway.
2022-07-07 11:12:12 +02:00
Johannes Becker 4b856ce9d6 fix(sdk): Use the local config variable to decide identity assertion 2022-07-07 10:16:23 +02:00
Ivan Enderlin c043daede0 test(crypto): Fix a test. 2022-07-07 10:15:24 +02:00
Ivan Enderlin 9f6988f766 Merge branch 'main' into fix-issue-796 2022-07-07 10:11:43 +02:00
Ivan Enderlin ed0709373d fix(crypto): Rename web_key to key for MediaEncryptionInfo.
Based on the [Section 11.11.1.6.1 Extensions to `m.room.message`
msgtypes](https://spec.matrix.org/v1.2/client-server-api/#extensions-to-mroommessage-msgtypes),
the parameter for the JSON Web Key is named `key`, not `web_key`. This
patch fixes that by renaming the field when serializing and
deserializing.
2022-07-07 10:04:34 +02:00
Ivan Enderlin acf9b15571 feat(bindings/crypto-nodejs): Use latest napi-rs version to avoid cloning Uint8Array.
The new `napi-rs` release includes a patch that avoids cloning and
copying data inside a `Uint8Array`
(https://github.com/napi-rs/napi-rs/pull/1224), it now returns a
“Node.js reference” of it.

This new `napi-rs` release also includes one of our patch,
https://github.com/napi-rs/napi-rs/pull/1200, which means we no longer
need to depend on our fork.
2022-07-07 09:49:34 +02:00
Charles Wright ba39185679 Fix build errors 2022-07-06 18:08:02 +02:00
Benjamin Kampmann dc40309cbe feat(sled): Introduce SledStoreBuilder, allow migration conflict strategy configuration 2022-07-06 17:51:51 +02:00
Doug da277c4978 Create a new client on login.
More clippy errors.
2022-07-06 12:43:02 +01:00
Doug 9925d73e7b Fix typos and clippy errors. 2022-07-06 11:52:33 +01:00
Benjamin Kampmann d465b70bea refactor(indexeddb)!: Rename SerializationError to IndexedDBStoreError 2022-07-06 12:18:58 +02:00
Doug fec879f0f3 Simplify AuthenticationError for now. 2022-07-06 10:29:13 +01:00
Doug 91427b82a5 Use an Optional client instead of failable init. 2022-07-05 18:14:25 +01:00
Ivan Enderlin d7739369ae chore(bindings/crypto-nodejs): Remove useless napi::Result. 2022-07-05 17:45:09 +02:00
Ivan Enderlin 4fd24eebea feat(bindings/crypto-nodejs): Implement an Attachment API.
This patch provides a new API to encrypt and decrypt attachment,
i.e. big buffer of type `Uint8Array`.

It's based on `matrix_sdk_crypto::AttachmentEncryptor` and `AttachmentDecryptor`.
2022-07-05 17:31:52 +02:00
Benjamin Kampmann 73daec3757 Merge pull request #810 from gnunicorn/expose-invite-details
feat(sdk): Expose details of invite for invited room
2022-07-05 14:56:05 +02:00
Benjamin Kampmann d9f3b257b4 Apply suggestions from code review
Co-authored-by: Ivan Enderlin <ivan@mnt.io>
2022-07-05 14:38:53 +02:00
Damir Jelić 771c33d710 chore(crypto): Bump vodozemac
Vodozemac used to accept and return strings when encrypting and
decrypting. This is quite unusual for a pure cryptographic library so we
switched towards the usual setup where we encrypt/decrypt raw bytes.

Since we do encrypt/decrypt JSON strings in Matrix land, we do the
string conversions over here.
2022-07-05 13:23:50 +02:00
Doug 56adf6a89b Add a client_container with locks. 2022-07-05 11:43:10 +01:00
Ivan Enderlin e5a7a975a3 feat(bindings/crypto-nodejs): Transform timeout into milliseconds. 2022-07-05 12:05:53 +02:00
Ivan Enderlin f3e69a2352 fix(bindings/cryto-nodejs): Fix memory corruption in async functions
fix(bindings/cryto-nodejs): Fix memory corruption in async functions
2022-07-05 10:25:16 +02:00
Ivan Enderlin 607d7ebc22 fix(bindings/cryto-nodejs): Fix memory corruption in async functions.
In async functions, the Node.js GC may or may not (that's a random
behavior) collect the arguments passed to the function as soon as it
returns. The function may not be executed yet, since it's async. Thus,
it leads to memory corruption: The function tries to read later on the
value inside an argument and… it crashes at best.

To avoid this bug, there is no other choice than cloning the values
before the function returns, in its “sync path” (so before any
transformation of an `.await` point into an “async block”).

The performance impact is not “massive”, I'm not sure it could be
noticeable easily since it is most of the time related to identifiers
(e.g. `UserId`), which are cheap to clone. I have to find the balance
here, and cloning offers the best trade off from my point of view.
2022-07-05 09:07:20 +02:00
Doug 0178b71437 Add basic AuthenticationService to the FFI. 2022-07-04 16:55:50 +01:00
Kévin Commaille dd6a902240 test(sdk): Move integration tests 2022-07-04 16:22:20 +02:00
Kévin Commaille 4eb1337dc8 ci: Remove whitespaces in config file 2022-07-04 16:22:19 +02:00
Benjamin Kampmann 81f02f0d0b Merge pull request #804 from gnunicorn/ben-hunting-the-nodjs-segfault
Hunting the nodejs segfault bug, long-term
2022-07-04 15:25:09 +02:00
Ivan Enderlin 76fe6d54ac feat(bindings/crypto-*): Add fallback_keys field to KeysUploadRequest
feat(bindings/crypto-*): Add `fallback_keys` field to `KeysUploadRequest`
2022-07-04 15:02:38 +02:00
Ivan Enderlin eb358889e9 Merge branch 'main' into fix-issue-800 2022-07-04 14:31:28 +02:00
Ivan Enderlin c82631c414 feat(bindings/crypto-js): Implement OlmMachine.sign
feat(bindings/crypto-js): Implement `OlmMachine.sign`
2022-07-04 14:16:27 +02:00
Ivan Enderlin fb4a940a26 chore: Make Clippy happy… 2022-07-04 13:50:28 +02:00
Ivan Enderlin 05561a8777 chore(crypto): Make Clippy happy. 2022-07-04 13:06:14 +02:00
Ivan Enderlin 909ada43d7 chore(base): Make Clippy happy.
So, Clippy suggests to change `(&member).into()` to `member.into()`
but it's the same, and `From<T>` is not implemented for this `T`, only
`From<&T>` is present. Thus, to deceive Clippy, I'm using
`std::borrow::Borrow` here. Not super happy with that though…
2022-07-04 12:00:42 +02:00
Ivan Enderlin 6176b3b658 feat(bindings/crypto-ffi): Add fallback_keys field to KeysUpload. 2022-07-04 11:49:45 +02:00
Ivan Enderlin 566227576e feat(bindings/crypto-js): Add fallback_keys field to KeysUploadRequest. 2022-07-04 11:47:27 +02:00
Ivan Enderlin d6c0ef1497 feat(bindings/crypto-nodejs): Add fallback_keys field to KeysUploadRequest. 2022-07-04 11:47:15 +02:00
Ivan Enderlin f72a14890d chore(crypto) Make Clippy happy. 2022-07-04 11:42:50 +02:00
Ivan Enderlin 62378b4abc Merge branch 'main' into fix-issue-797 2022-07-04 11:24:33 +02:00
Ivan Enderlin f96069f591 chore(store-encryption): Call DerefMut manually.
Clippy on nigtly is raising a warning, which is turned into an error
on the CI. It's the [`explicit_auto_deref`
lint](https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref). I
suspect it's a false-positive but I'm not sure. Anyway, to workaround
this and unblock our CI, let's call `DerefMut::deref_mut` manually:
it's clearer anyway.
2022-07-04 11:00:23 +02:00
Ivan Enderlin 59615d4ae3 chore(bindings/crypto-nodejs): Clean up based on feedback. 2022-07-04 10:17:11 +02:00
Benjamin Kampmann fd38c757e4 feat(sdk): Expose details of invite for invited room 2022-07-01 19:44:27 +02:00
Jonas Platte 861d899541 refactor(base): Remove an unnecessary allocation 2022-07-01 16:16:29 +02:00
Jonas Platte fd08c9e7da refactor(base): Remove check for own user in notification handling
This is now done in Ruma.
Reverts commit bc78095611.
2022-07-01 16:16:29 +02:00
Jonas Platte cffb565a5f chore: Allow some usage of deprecated fields
… to allow CI to succeed. They should be removed soon.
2022-07-01 16:16:29 +02:00
Jonas Platte f20d1c3d76 chore: Upgrade ruma 2022-07-01 16:16:29 +02:00
Jonas Platte e4f6c0cc58 chore(sdk): Remove feature ruma/appservice-api-helper
No longer used as of https://github.com/matrix-org/matrix-rust-sdk/pull/710
2022-07-01 16:16:29 +02:00
Jonas Platte d3ae99eb22 chore: Silence new clippy lint 2022-07-01 12:39:46 +02:00
Jonas Platte bc47caa356 chore: Remove unnecessary map_err's 2022-07-01 11:55:28 +02:00
Ivan Enderlin afa96f1bf4 test(bindings/crypto-nodejs): Add more signing test cases. 2022-06-30 16:58:39 +02:00
Ivan Enderlin c99f42347c chore(bindings/crypto-nodejs): Simplify code by removing matches!. 2022-06-30 16:52:08 +02:00
Ivan Enderlin 51cb35502d doc(bindings/crypto-nodejs): Add missing documentation. 2022-06-30 16:50:33 +02:00
Ivan Enderlin b59077e83d chore(bindings/crypto-nodejs): Replacing into_iter by iter on &BTreeMap.
Calling `into_iter` on `&BTreeMap` will not consume it. It has the
same effect as calling `iter`. So let's do it.
2022-06-30 16:48:44 +02:00
Ivan Enderlin 3f197734d9 feat(bindings/crypto-nodejs) Implement OlmMachine.sign.
This patch first implements the new `Signatures`, `Signature` and `MaybeSignature` types.

Then, it moves some Vodozemac types into their own module, and
implements the new `Ed25519Signature` type.

Finally, it implements `OlmMachine.sign`.
2022-06-30 16:44:07 +02:00
Benjamin Kampmann 1961403512 ci(crypto-nodejs): Only build non-release version on failure, improve CI build time 2022-06-30 12:53:51 +02:00
Benjamin Kampmann f1ebbfd245 ci(crypto-nodejs): Create non-release build of version and upload everything as artifacts upon failure 2022-06-30 12:24:21 +02:00
Ivan Enderlin 0458ed9be1 feat(bindings/crypto-js): Implement DeviceKeyId, DeviceKeyAlgorithm and DeviceKeyAlgorithmName. 2022-06-30 08:51:36 +02:00
Ivan Enderlin 12c7b76fea feat(bindings/crypto-js): Implement `OlmMachine.crossSigningStatus. 2022-06-30 08:33:28 +02:00
Marcel a8601e186a fix(appservice): Don't process the same transaction twice 2022-06-29 16:17:25 +00:00
Stefan Ceriu 8a2d13feea feat(bindings): Session verification through FFI 2022-06-29 13:59:52 +02:00
Benjamin Kampmann e2ca56114e Merge pull request #785 from zecakeh/room-permalink
feat(sdk): Add method to get a room permalink
2022-06-29 13:58:25 +02:00
Anderas 3c6d159a04 refactor: Use ClientBuilder pattern in SDK FFI
Co-authored-by: Jonas Platte <jplatte@matrix.org>
2022-06-29 13:13:31 +02:00
Benjamin Kampmann 464bc43290 Merge pull request #793 from Hywan/test-crypto-nodejs-timeout
test(crypto-nodejs): Increase timeout
2022-06-29 12:58:11 +02:00
Kévin Commaille 297861e186 Fix docs styling 2022-06-29 12:20:57 +02:00
Kévin Commaille 8313029e33 Split into methods for both Matrix URI formats 2022-06-29 12:02:26 +02:00
Ivan Enderlin 913bdd683e feat(crypto-js): Change the package name
feat(crypto-js): Change the package name
2022-06-28 19:28:58 +02:00
Ivan Enderlin 041b9bc405 feat(crypto-js): Change the package name. 2022-06-28 17:05:21 +02:00
Ivan Enderlin 1526f76686 test(crypto-nodejs): Increase timeout.
For some unknown reasons, sometimes, randomly, one test (initializing
an `OlmMachine` with a local store with a passphrase) can take more
than 5s, only on Github Actions. Let's increase the test timeout value
so that the entire test suite doesn't fail.
2022-06-28 16:47:46 +02:00
Kévin Commaille f0e0194ff2 feat(sdk): Add method to get a room permalink
Include routing for room IDs
2022-06-26 13:38:01 +02:00
Kévin Commaille ebc7177438 feat(base): Add method to get Room alt aliases 2022-06-26 12:01:51 +02:00
Ivan Enderlin 091fab8a2a chore(bindings): Move matrix-sdk-ffi and matrix-sdk-crypto-ffi into the bindings/ directory
chore(bindings): Move `matrix-sdk-ffi` and `matrix-sdk-crypto-ffi` into the `bindings/` directory
2022-06-23 15:54:32 +02:00
Ivan Enderlin 818d715395 chore: Implement feedback. 2022-06-23 15:53:19 +02:00
Ivan Enderlin 5a0089da52 doc(bindings): Mention bindings in the top README.md file. 2022-06-23 15:12:51 +02:00
Ivan Enderlin 68b6c19dd4 test: Ensure all crates members of the workspace are compiled & tested. 2022-06-23 14:10:31 +02:00
Ivan Enderlin c29e2b9563 !fixup 2022-06-23 11:35:42 +02:00
Ivan Enderlin ecc28efd53 chore(bindings): Move matrix-sdk-ffi and matrix-sdk-crypto-ffi into the bindings/ directory. 2022-06-23 11:31:59 +02:00
Jonas Platte f3a61020e7 refactor(sdk): Rewrite sso login to be easier to read 2022-06-22 17:17:52 +02:00
Jonas Platte a423e92246 chore: Consistently capitalize 'device ID' 2022-06-22 17:17:52 +02:00
Jonas Platte b5d7f10c6b feature: Introduce a login builder API
This improves the readability of login calls.
The old login API is kept, but deprecated.
2022-06-22 17:17:52 +02:00
Ivan Enderlin 931eabf55c chore(bindings): Move crypto-nodejs and crypto-js into the bindings/ directory
chore(bindings): Move `crypto-nodejs` and `crypto-js` into the `bindings/` directory
2022-06-22 16:33:20 +02:00
Ivan Enderlin 8cd7fa9fb0 chore: Implement feedback. 2022-06-22 16:03:37 +02:00
Ivan Enderlin 1604f24136 chore(test): Fix YAML. 2022-06-22 16:03:37 +02:00
Ivan Enderlin 3da737b9e2 chore(test): Shorten job name for test-matrix-sdk-crypto-nodejs.
In the Github UI, we can only see:

    🐧 [m]-crypto-nodejs, Node.js…

What interests us is the Node.js version number.
2022-06-22 16:03:37 +02:00
Ivan Enderlin 2ffcc1a415 chore: Use [m] as an alias for matrix-sdk. 2022-06-22 16:03:37 +02:00
Ivan Enderlin 829dab42c5 chore(test): Rename the test-matrix-sdk-crypto-js job. 2022-06-22 16:03:37 +02:00
Ivan Enderlin 54acd314cc chore(test): Move the wasm workflow inside the ci workflow. 2022-06-22 16:03:37 +02:00
Ivan Enderlin 74953031ee chore(test): Use os-name in step name for test-appservice. 2022-06-22 16:03:37 +02:00
Ivan Enderlin 0436eb9349 chore(ci): Rephrase a little bit the Github Actions steps. 2022-06-22 16:03:37 +02:00
Ivan Enderlin a23bb8f5a0 chore(docs): Rephrase a little bit the Github Actions steps. 2022-06-22 11:57:41 +02:00
Ivan Enderlin 8db58986fb chore(bindings): Move crypto-nodejs and crypto-js into the bindings/ directory.
`matrix-sdk-crypto-nodejs` and `matrix-sdk-crypto-js` are no longer
default members of the Cargo virtual workspace. The Github Actions
workflows for the bindings now live in a `bindings_ci.yml` files
(ideally, it should be in a subdirectory,
`.github/workflows/bindings/ci.yml` but it doesn't work).
2022-06-22 11:54:49 +02:00
Ivan Enderlin 6ad323bc4e test: Run tests faster with nextest
test: Run tests faster with `nextest`
2022-06-22 09:56:02 +02:00
Ivan Enderlin b0d51fdfa5 test: There is no doctest for matrix-sdk-crypto-ffi. 2022-06-22 09:26:51 +02:00
Ivan Enderlin 3bfc68d476 test: Add missing cargo-nextest installation.
This patch also changes the step's name from Clippy to Test.
2022-06-22 09:26:51 +02:00
Ivan Enderlin eb33333925 test: Run doctests manually.
`cargo-nextest` doesn't support doctests for now, so we must run them
“manually” by running a separate `cargo test --doc` command.
2022-06-22 09:26:51 +02:00
Ivan Enderlin d9475c131a test(xtask): Remove xtask -- ci test as it is unused. 2022-06-22 09:26:48 +02:00
Ivan Enderlin 399862d955 test: Run tests faster with nextest.
> [`cargo-nextest`](https://nexte.st/index.html) is a next-generation
> test runner for Rust projects.

This patch installs and uses `nextest` to run our own tests.

Comparing `cargo test` and `cargo nextest` with hyperfine provides the
following results:

```sh
$ hyperfine 'cargo test --workspace' 'cargo nextest run --workspace && cargo test --doc'
Benchmark 1: cargo test --workspace
  Time (mean ± σ):     51.785 s ±  2.066 s    [User: 183.471 s, System: 10.563 s]
  Range (min … max):   49.151 s … 56.641 s    10 runs

Benchmark 2: cargo nextest run --workspace && cargo test --doc
  Time (mean ± σ):     44.556 s ±  0.894 s    [User: 192.213 s, System: 11.441 s]
  Range (min … max):   43.170 s … 45.762 s    10 runs
```

Benchmark 2 is 1.16 times faster than Benchmark 1.
2022-06-22 09:26:07 +02:00
Ivan Enderlin 2c1f5fed8d feat(crypto-js): Migrate tests and polish the API
feat(crypto-js): Migrate tests and polish the API
2022-06-21 12:04:45 +02:00
Ivan Enderlin 8b2237fa7a Merge branch 'main' into feat-crypto-js-next 2022-06-21 11:38:48 +02:00
Ivan Enderlin e5ea2a770b chore(crypto-js): Implement feedback from PR. 2022-06-21 11:25:58 +02:00
Jonas Platte 5f31e9d131 chore: Add missing json language specification to docs 2022-06-20 22:33:35 +02:00
Jonas Platte 6cb9c11b88 chore: Remove unnecessary pub visibility from OnceCell imports 2022-06-20 22:33:35 +02:00
Jonas Platte a00c130fc3 feature: Allow passing already-Arc'ed stores to StoreConfig methods 2022-06-20 18:00:33 +02:00
Jonas Platte 4971802e75 chore: Replace usage of Store with Arc<dyn StateStore> 2022-06-17 17:35:33 +02:00
Jonas Platte 8690addfd5 chore: Add Clone impl for ClientBuilder 2022-06-17 14:59:22 +02:00
Jonas Platte 00a20f325b chore: Add Clone impl for StoreConfig
… by storing the stores inside Arc's instead of Box'es.
2022-06-17 14:59:22 +02:00
Jonas Platte a4e4bfe833 refactor(sdk)!: Change store builder methods from Box<dyn Trait> to impl Trait
To migrate, don't box the store before passing it to `builder.state_store` or
`builder.crypto_store` (remove `Box::new`).
2022-06-17 14:59:22 +02:00
Jonas Platte 02aa537f2a chore: Keep uniffi version in sync across deps, CI 2022-06-17 13:37:07 +02:00
Benjamin Kampmann 5316d2e6e7 fix(indexeddb): Upgrade version to latest state store pattern 2022-06-16 13:17:45 +02:00
Benjamin Kampmann 7adef1d24e fix(sled): Upgrade db version to latest changes 2022-06-16 13:17:35 +02:00
Anderas c755e19fb3 Merge pull request #765 from matrix-org/andy/crypto_ffi
Build Crypto iOS framework
2022-06-16 11:25:39 +01:00
Jonas Platte 8250c24525 chore: Undo pinning of clap 2022-06-15 20:47:55 +02:00
Andy Uhnak fe29fa57eb Build Crypto iOS framework 2022-06-15 13:52:21 +01:00
Ivan Enderlin c564b1a5e1 feat(crypto-nodejs): Add store_path and store_passphrase to the OlmMachine constructor
feat(crypto-nodejs): Add `store_path` and `store_passphrase` to the `OlmMachine` constructor
2022-06-15 07:28:29 +02:00
Benjamin Kampmann 9d691e238a Merge pull request #759 from Hywan/fix-codecov-labs
chore(test): Remove `labs` from the projects + exclude `matrix-sdk-indexeddb` from code coverage report
2022-06-14 20:29:07 +02:00
Ivan Enderlin 520e2f30f7 doc(crypto-js): Add missing module documentation. 2022-06-14 16:54:22 +02:00
Ivan Enderlin c56ab5928c test(crypto-js): Add a workflow to test matrix-sdk-crypto-js. 2022-06-14 16:34:08 +02:00
Ivan Enderlin f8cd2310be feat(crypto-js): Implement OlmMachine.decryptRoomEvent & siblings. 2022-06-14 16:16:54 +02:00
Ivan Enderlin 073fb45580 feat(crypto-nodejs): Define Node.js versions policy.
We now support only “current”, “active” or “maintenance” versions
according to https://nodejs.org/en/about/releases/, which are
compatible with NAPI v6.
2022-06-14 16:05:01 +02:00
Ivan Enderlin 3833d35348 chore(crypto-nodejs): Drop Node.js v12.17.
There is a segfault with `napi-rs` and Node.js in v12.17. It's an old
version, it may be fair to drop its support for now. Let's see if
people would need it in the future, we may work on `napi-rs` to fix
this bug in case it's really necessary.
2022-06-14 16:05:01 +02:00
Ivan Enderlin 83b730d1c8 chore(crypto-nodejs): Make the code compatible with Node.js < 18. 2022-06-14 16:05:01 +02:00
Ivan Enderlin 6477cc5072 feat(crypto-nodejs): Add store_path and store_passphrase to the OlmMachine constructor.
This patch adds the `store_path` and the `store_passphrase` arguments
to the `OlmMachine` constructor to use a `CryptoStore` instead of
having an in-memory Olm machine.
2022-06-14 16:05:01 +02:00
Ivan Enderlin 2117b36a75 chore(test): Exclude matrix-sdk-indexeddb from code coverage report. 2022-06-14 16:04:41 +02:00
Ivan Enderlin 5e8ed3bcbf chore(test): Remove labs from the projects.
It's already part of the `ignore` section.
2022-06-14 16:04:41 +02:00
Jonas Platte 87639a4c4c fix(appservice): Remove erroneous ? operator 2022-06-14 15:20:24 +02:00
Amanda Graven de04aba5b3 fix(appservice): Remove erroneous ? operator 2022-06-14 15:02:55 +02:00
Amanda Graven 5243091bec test(appservice): Virtual client membership
Test that virtual clients get assigned the correct membership to rooms
when processing received transactions.
2022-06-14 14:39:54 +02:00
Amanda Graven 1d746f1ef1 fix(appservice): Virtual client non-membership
Don't assume virtual client membership is join if none is stored, since
that leads to a client being told it's joined in rooms it has no
membership of. The main appservice client still assumes it's joined
every room it receives transaction events about.
2022-06-14 14:39:54 +02:00
Ivan Enderlin 56d74e25b8 test(crypto-js): Finish to migrate the test suites + code clean up. 2022-06-14 14:09:52 +02:00
Ivan Enderlin a758d98f84 feat(crypto-nodejs): Update license. 2022-06-14 12:12:27 +02:00
Ivan Enderlin 5adae6fd41 feat(crypto-js): Migrate tests and polish the API. 2022-06-14 12:12:27 +02:00
Damir Jelić 38d771cca6 ci(coverage): Set the correct out format for CI coverage reports 2022-06-14 12:01:03 +02:00
Jonas Platte dd4c329f57 chore: Prevent clap upgrades beyond 3.2 2022-06-14 11:02:19 +02:00
Jonas Platte e3edf0139a Enable rustdoc-map nightly feature via .cargo/config.toml
… instead of using -Z on the command line.
2022-06-13 14:30:10 +02:00
Jonas Platte d07001a581 chore: Work around a cargo bug 2022-06-13 14:30:10 +02:00
Ivan Enderlin 62b8169ac2 chore(test): Ajust code coverage configurations (tarpaulin and codecov)
chore(test):  Code Coverage must ignore some `matrix-sdk-crypto-(js|nodejs)`
2022-06-13 14:24:22 +02:00
Ivan Enderlin d35063412f chore(test): Ignore matrix-sdk-test-macros and matrix-sdk-ffi. 2022-06-13 12:52:37 +02:00
Damir Jelić 1a162e5dd5 test(crypto): Test the double verification cancellation 2022-06-13 12:30:52 +02:00
Damir Jelić b8069af8ba fix(crypto): Cancel the verification flow if we multiple verifications
The spec claims that we should cancel verifications if multiple
verifications are attempted at once[1]:
    When the same device attempts to initiate multiple verification
    attempts, the recipient should cancel all attempts with that device.

So let's start doing this.

[1]: https://spec.matrix.org/v1.2/client-server-api/#error-and-exception-handling
2022-06-13 12:30:52 +02:00
Damir Jelić fa3e192c37 docs(crypto): Improve the signature verification docs some more
Co-authored-by: Denis Kasak <dkasak@termina.org.uk>
2022-06-13 12:12:09 +02:00
Damir Jelić efc53569ed refactor(crypto): Rename our is_signed_by methods 2022-06-13 12:12:09 +02:00
Damir Jelić 5c12132569 refactor(crypto): Introduce a SignedJsonObject trait
This should mostly remove the wild west of signature verification. We
define a trait that tells us which objects can contain signatures and
thus can be passed on to signature verification methods.

It also should be slightly more efficient, since we removed a bunch of
duplicate canonicalization steps.
2022-06-13 12:12:09 +02:00
Amanda Graven 251a38285c perf(appservice): Cache namespace regexes 2022-06-13 11:57:50 +02:00
Ivan Enderlin b500fa1daa chore(test): Configure Tarpaulin to collect data from matrix-sdk-common. 2022-06-13 11:27:22 +02:00
Ivan Enderlin 4430dae421 chore(test): Configure Tarpaulin to ignore more crates. 2022-06-13 11:25:54 +02:00
Ivan Enderlin eead09984c chore(test): Ask codecov.io to ignore the crates/matrix-sdk-crypto-ffi directory. 2022-06-13 11:25:38 +02:00
Ivan Enderlin dcfcac0bd3 chore(test): Ask codecov.io to ignore labs and xtask directories. 2022-06-13 11:24:10 +02:00
Jonas Platte dc32fc282d Remove sync_token from BaseClient
… as it is also accessible as self.store.sync_token in BaseClient methods.
2022-06-13 11:10:12 +02:00
Jonas Platte b6eed09564 Make session field in Store private
… for cleaner encapsulation.
2022-06-13 11:10:12 +02:00
Jonas Platte 29ba171953 Simplify ownership of server_versions
We couldn't originally take references to it when it was behind RwLock,
with OnceCell this is no longer a problem.
2022-06-13 11:10:12 +02:00
Jonas Platte 9e9152745c Remove homeserver_url from HttpClient 2022-06-13 11:10:12 +02:00
Jonas Platte 12d1607cdc Remove session from HttpClient 2022-06-13 11:10:12 +02:00
Jonas Platte 6c8b520f14 Remove unused Clone impls on private types 2022-06-13 11:10:12 +02:00
Jonas Platte fedcdb1e63 chore: Add some more tracing events 2022-06-13 11:10:12 +02:00
Jonas Platte 1cfd69a880 chore: Improve doc comment formatting 2022-06-13 11:10:12 +02:00
Ivan Enderlin dad035d170 chore(test): Configure tarpaulin to use its config file. 2022-06-13 10:46:29 +02:00
Ivan Enderlin 3c14acf163 chore(test): Exclude matrix-sdk-crypto-(js|nodejs) from code coverage reports. 2022-06-13 10:38:07 +02:00
Ivan Enderlin 253affeb0c feat(crypto-nodejs) Implement missing APIs
feat(crypto-nodejs) Implement missing APIs
2022-06-09 21:38:41 +02:00
Ivan Enderlin fe4ddfde89 chore(crypto-nodejs): Remove clone calls when possible. 2022-06-09 21:15:25 +02:00
Ivan Enderlin 8a332ca9e1 chore(crypto-nodejs): Implement feedbacks / polish. 2022-06-09 18:02:29 +02:00
Ivan Enderlin 506f57a22c feat(crypto-nodejs): Enable traace filtering and change the env var to MATRIX_LOG. 2022-06-09 14:06:42 +02:00
Damir Jelić 8b05f9276f fix(sdk): Remove a duplicate Session cell from the HttpClient 2022-06-09 13:37:42 +02:00
Damir Jelić e8b2655d52 docs(crypto-ffi): Explain what a timeout of 0 means when fetching identities 2022-06-09 11:58:23 +02:00
Damir Jelić 537ef1409b test(crypto): Test that we're now notifying when we receive a /keys/query 2022-06-09 11:58:23 +02:00
Damir Jelić e2bf3d0d18 chore(crypto-js): Silence some doc warnings 2022-06-09 11:58:23 +02:00
Damir Jelić d188ef3386 feat(crypto): Add a way wait for a keys/query to be done when fetching identities. 2022-06-09 11:58:23 +02:00
Damir Jelić ba7ccb40cc feat(crypto): Wait for a key query to be done if we're claming one-time keys 2022-06-09 11:58:23 +02:00
Ivan Enderlin 1b2c644277 test(crypto-nodejs): Set up CI to run the test suites. 2022-06-09 11:34:16 +02:00
Benjamin Kampmann 6a853d0173 Merge pull request #744 from matrix-org/jplatte/readme
chore: Fix `main` docs link in readme
2022-06-09 10:51:58 +02:00
Jonas Platte 7487b24fe3 chore: Fix main docs link in readme 2022-06-09 10:21:04 +02:00
Amanda Graven 5c4f2b3430 fix(appservice): Make membership keys consistent
Correct the inconsistency between the keys used for reading and writing
the membership of a virtual user in the appservice's namespace
2022-06-08 10:42:35 +02:00
Stefan Ceriu 901b670a22 Use the nightly toolchain together with the newly introduced target-applies-to-host on the sdk-ffi crate debug builds to avoid cache corruption issues. Switched back to debug mode as the internal tokio crashes went away 2022-06-08 08:55:28 +02:00
Jonas Platte 28d6e2821b chore: Replace new usage of Box<UserId> 2022-06-07 21:12:43 +02:00
Jonas Platte 7f0b74a39d fixup! style: Remove usage of assign! macro from doctests 2022-06-07 21:07:34 +02:00
Jonas Platte 7b6869310f style: Make assign! formatting consistent 2022-06-07 21:07:34 +02:00
Jonas Platte 445b2e627d style: Remove usage of assign! macro from examples 2022-06-07 21:07:34 +02:00
Jonas Platte 0e206506d9 style: Remove usage of assign! macro from doctests 2022-06-07 21:07:34 +02:00
Jonas Platte 0fb1d72100 style: Use anyhow::Ok in more places 2022-06-07 21:07:34 +02:00
Ivan Enderlin 6d8c54deb5 chore(crypto-nodejs): Add missing newline. 2022-06-07 17:03:02 +02:00
Ivan Enderlin 07620452df chore(crypto-node): Make Clippy happy. 2022-06-07 16:50:26 +02:00
Jonas Platte 099db20555 Use target-applies-to-host to avoid unnecessary cache invalidation 2022-06-07 16:43:17 +02:00
Ivan Enderlin 75571c2c30 fix(crypto-js): Fix missing symbol. 2022-06-07 16:42:34 +02:00
Ivan Enderlin 8c95b1c4cf chore(crypto-nodejs): Fix a typo. 2022-06-07 16:40:54 +02:00
Ivan Enderlin 530c268e61 doc(crypto-nodejs): Include the README.md in the generated documentation. 2022-06-07 16:35:22 +02:00
Ivan Enderlin 99dcf84340 chore(crypto-nodejs): Use napi::Result when possible. 2022-06-07 16:28:55 +02:00
Ivan Enderlin 2a76d17bd9 doc(crypto-nodejs): Generate JavaScript/TypeScript documentation. 2022-06-07 16:26:23 +02:00
Ivan Enderlin 15af364c97 Merge branch 'main' into feat-crypto-nodejs-next 2022-06-07 15:31:03 +02:00
Ivan Enderlin ec7724f393 doc(crypto-nodejs): Improve the README.md. 2022-06-07 15:27:35 +02:00
Ivan Enderlin 1e7d509920 chore(crypto-nodejs): Clean up. 2022-06-07 14:45:20 +02:00
Ivan Enderlin 520758bf1e test(crypto-nodejs): Finish the OlmMachine test suite. 2022-06-07 14:42:24 +02:00
Benjamin Kampmann 7f49618d35 Merge pull request #729 from gnunicorn/gnunicorn/issue609
Ensure all data state stores save is encrypted
2022-06-07 12:41:38 +02:00
Benjamin Kampmann a308771a7a fix(sled): Deserialize encrypted TimelineMetadata properly 2022-06-07 12:22:54 +02:00
Benjamin Kampmann 2c4379909c fix(sled): Wrap [u8] encoding in an explicit type to prevent accidential misuse 2022-06-07 12:22:54 +02:00
Damir Jelić d4f49ca334 chore: Fix some new clippy warnings 2022-06-07 11:41:29 +02:00
Damir Jelić c97bb83af9 refactor(base): Remove a bunch of OlmMachine wrapper methods
The whole machine is nowadays exposed through the base Client, so no
need to re-expose individual methods that don't add more functionality.
2022-06-07 11:41:29 +02:00
Ivan Enderlin 12c53bb2bc test(crypto-nodejs): Add more test cases. 2022-06-07 11:05:19 +02:00
Damir Jelić 7fd973c563 refactor(crypto-ffi): Make use of the BackupRecoveryKey type instead of a string 2022-06-07 10:47:19 +02:00
Benjamin Kampmann f8dae723c3 style: Fix review remarks 2022-06-07 10:20:55 +02:00
Benjamin Kampmann 8ccf78c025 fix(indexeddb): Ensure all values are encrypted (namely filters and timeline metadata) if asked to 2022-06-07 10:20:40 +02:00
Benjamin Kampmann 47bb73cf89 fix(sled): Ensure timeline metadata is also saved encrypted 2022-06-07 10:20:27 +02:00
Benjamin Kampmann f3d952839e style: Clean up rustfmt and clippy 2022-06-07 10:19:43 +02:00
Benjamin Kampmann 85ea9279dc fix(indexeddb): Use random db in plain test for consistency 2022-06-07 10:19:27 +02:00
Benjamin Kampmann be159356cd fix(sled): Encrypt custom key and value 2022-06-07 10:19:23 +02:00
Benjamin Kampmann 571b5e61cf fix(sled): Encrypt media content 2022-06-07 10:19:20 +02:00
Benjamin Kampmann 77af11bcbc fix(sled): Encrypt saved sync_token 2022-06-07 10:19:20 +02:00
Benjamin Kampmann 335251695a fix(sled): Encrypt saved filters 2022-06-07 10:19:17 +02:00
Benjamin Kampmann bf17012d6b test(base): Add integration store test for saving filters 2022-06-07 10:19:13 +02:00
Benjamin Kampmann 328ebdba9c fix(sled): Encrypt saved user_ids 2022-06-07 10:19:08 +02:00
Benjamin Kampmann c359b011fa refactor(sled): Rename for consistency event -> value 2022-06-07 10:19:04 +02:00
Ivan Enderlin 07fed7f4df test(crypto-nodejs): Continue to test OlmMachine and add tracing support. 2022-06-06 16:27:32 +02:00
Ivan Enderlin 0ed74d8c31 test(crypto-nodejs): Test OlmMachine.receiveSyncChanges, .outgoingRequests and .markRequestAsSent. 2022-06-06 12:09:37 +02:00
Ivan Enderlin 35d7cab330 feat(crypto-nodejs): Rename requests request_id to id + add type. 2022-06-06 12:08:58 +02:00
Ivan Enderlin 407e27d176 feat(crypto-nodejs): Make changed and left optional in DeviceLists constructor. 2022-06-06 10:27:32 +02:00
Damir Jelić 0f758a643c refactor(crypto): Make the boolean parameter for the backup verification clearer 2022-06-03 16:29:17 +02:00
Damir Jelić fd2ae1ed8f docs(crypto): Improve some backup verfication docs
Co-authored-by: Denis Kasak <dkasak@termina.org.uk>
2022-06-03 16:29:17 +02:00
Damir Jelić 2b13c0832f refactor(crypto): Introduce a struct for the backup info 2022-06-03 16:29:17 +02:00
Damir Jelić b3fbd15270 feat(crypto): Improve the API to verify backups
This API change allows us to inspect why a backup is considered to be
trusted instead of just returning a boolean telling us if it's trusted
or not.
2022-06-03 16:29:17 +02:00
Amanda Graven 4e3e393596 refactor(appservice): Better virtual client sync
Store the membership state of clients in the appservice's namespace, and
construct sync events based on that information
2022-06-03 15:03:31 +02:00
Jonas Platte 7f81e7c61b chore: Optimize quote even in debug mode 2022-06-03 14:48:48 +02:00
Ivan Enderlin a75ae16b79 test(crypto-nodejs): Add more test suites. 2022-06-02 15:58:30 +02:00
Ivan Enderlin 9ebc61ad0f test(crypto-nodejs): Adding more test suites. 2022-06-02 15:24:22 +02:00
Ivan Enderlin 0d66480cd6 fix(crypto-nodejs): OlmMachine.new effectively raises an error.
Returning an `napi::Error` doesn't raise it. We must return a
`Result<_, napi::Error>` to ensure `napi` will raise the error as
expected.
2022-06-02 15:23:25 +02:00
Ivan Enderlin 02802e9088 feat(crypto-nodejs): EncryptionSettings.rotation_period and .…_period_messages are now BigInt. 2022-06-02 13:59:00 +02:00
Ivan Enderlin 1a6adc6d41 chore(crypto-nodejs): Ignore package-lock.json for now. 2022-06-02 11:10:55 +02:00
Ivan Enderlin 6d10d6150c test(crypto-nodejs): Add some test suites. 2022-06-02 11:09:57 +02:00
Damir Jelić fb1a2f6d94 feat(appservice): Add support for appservice login
An appservice can log in by providing a valid appservice token and a user within the appservice’s namespace.

This allows the appservice to acquire a scoped access token for a single user. matrix-appservice-sdk can now take a persisted device id for virtual users. If e2ee is enabled, it will automatically perform an appservice login to create the an E2EE backed session for the virtual user.
2022-06-02 10:36:33 +02:00
Damir Jelić bdee4e0547 refactor(crypto): Use a result type for invalid signatures 2022-06-02 09:31:37 +02:00
Denis Kasak 42d5eec8b7 docs(crypto): Improve Signature doc comment. 2022-06-02 09:31:37 +02:00
Damir Jelić e50c1465ea docs(crypto): Improve some signature verification related docs
Co-authored-by: Denis Kasak <dkasak@termina.org.uk>
2022-06-02 09:31:37 +02:00
Damir Jelić 5827cc9a39 refactor(crypto): Use ? instead of nesting closures when searching for a key
Co-authored-by: Jonas Platte <jplatte@element.io>
2022-06-02 09:31:36 +02:00
Damir Jelić c95fd93666 fix(types): Allow Ed25519 signatures to be invalid
Since signatures can be uploaded by anyone, unlike one-time keys which
might only be uploaded by the given device for themselves, a Signatures
struct might fail to be deserialized because a signature was maliciously
attached to the object by someone else.

We don't want to fail to deserialize all the signatures just because
someone attached an invalid one. We still would like to have the
signature be deserialized into its proper type if possible so the Raw<T>
pattern was not used.
2022-06-02 09:31:36 +02:00
Damir Jelić d96fcf7199 refactor(crypto): Return a Signatures struct in the sign method 2022-06-02 09:31:36 +02:00
Damir Jelić e2348471db fix(crypto): Make fetching of the first cross signing key more robust 2022-06-02 09:31:36 +02:00
Ivan Enderlin 8e120eb648 doc(crypto-nodejs): Write missing documentation. 2022-06-02 09:23:26 +02:00
Ivan Enderlin c573985c64 chore(crypto-nodejs): Add a .gitignore for artifacts. 2022-06-02 09:19:17 +02:00
Ivan Enderlin 6a996c7657 doc(crypto-nodejs): Write missing documentation and clean up the code. 2022-06-02 09:18:26 +02:00
Ivan Enderlin 7bb96e0ece chore(crypto-node): Use Either7 (new pending feature). 2022-06-01 22:38:52 +02:00
Ivan Enderlin dc7f0389b1 chore(crypto-node): Document and add napi(getter) when necessary. 2022-06-01 20:55:34 +02:00
Ivan Enderlin 5d4b3a0457 feat(crypto-nodejs): Implement OlmMachine.decrypt_room_event. 2022-06-01 20:55:34 +02:00
Ivan Enderlin 888ffb0a56 chore(crypto-node): Use Either5 + Either3 instead of Either. 2022-06-01 20:55:34 +02:00
Ivan Enderlin 475fffb64d feat(crypto-nodejs): Implement OlmMachine.encrypt_room_event. 2022-06-01 20:55:34 +02:00
Ivan Enderlin a8b44f26ea feat(crypto-nodejs): Implement OlmMachine.share_room_key. 2022-06-01 20:55:34 +02:00
Ivan Enderlin a7697fbd32 feat(crypto-nodejs): Implement OlmMachine.get_missing_sessions. 2022-06-01 20:55:34 +02:00
Ivan Enderlin 02e63ab9c1 chore(crypto-js): Remove a useless import. 2022-06-01 20:55:34 +02:00
Ivan Enderlin 8d909ccabe feat(crypto-nodejs): Implement OlmMachine.mark_request_as_sent. 2022-06-01 20:55:34 +02:00
Ivan Enderlin 2e22f6b6c0 feat(crypto-nodejs): Implement OlmMachine.outgoing_requests. 2022-06-01 20:55:34 +02:00
Ivan Enderlin 7c85fdaf28 doc(crypto-js): Update documentation. 2022-06-01 20:55:34 +02:00
Ivan Enderlin 1ecf90bb42 feat(crypto-nodejs): Implement OlmMachine.receive_sync_changes. 2022-06-01 20:55:34 +02:00
Ivan Enderlin e2ecba7d45 feat(crypto-nodejs): Implement OlmMachine.update_tracked_users. 2022-06-01 20:55:34 +02:00
Ivan Enderlin 8a0e0a7a4d feat(crypto-nodejs): Start implementing OlmMachine API. 2022-06-01 20:55:34 +02:00
Charlotte 🦝 Delenk c562d91533 feat(appservice): Use appservice logins for e2ee
This commit also adds a virtual user builder, which allows you to
control the device id, change the client builder, perform an appservice
login, and even restore a previously created session.

https://spec.matrix.org/v1.2/client-server-api/#appservice-login
2022-06-01 15:29:04 +01:00
Ivan Enderlin 3cf4150df2 feat: Rename OlmMachine.share_group_session to OlmMachine.share_room_key
feat: Rename `OlmMachine.share_group_session` to `OlmMachine.share_room_key`
2022-06-01 15:08:58 +02:00
Ivan Enderlin b3d52ca68c feat: Rename OlmMachine.share_group_session to OlmMachine.share_room_key.
As discussed with @poljar, the Matrix event we are creating is
`m.room.key`, so it's preferable to rename the function
`share_room_key`. Note: Olm calls the things an inbound group session,
that's where the name comes from, but it's not aligned with the
specification.
2022-06-01 14:32:11 +02:00
Ivan Enderlin e673954b98 chore: Add missing new lines at the end of files
chore: Add missing new lines at the end of files
2022-05-31 10:31:08 +02:00
Ivan Enderlin 6d7573dced chore: Add missing new lines at the end of files. 2022-05-31 10:06:35 +02:00
Ivan Enderlin 2d14452398 Merge pull request #675 from Hywan/feat-crypto-wasm
feat(crypto): Port to Wasm (in a JS host) and to NodeJS
2022-05-31 10:01:57 +02:00
Ivan Enderlin 7931c4a589 chore(crypto-js): Clean up code and make CI happy. 2022-05-31 08:44:36 +02:00
Ivan Enderlin 4db1ad350b feat(crypto-nodejs): Derive Debug for UserId. 2022-05-31 08:40:16 +02:00
Ivan Enderlin 0debbf24d7 chore(crypto-js): Remove an unknown Clippy lint for now. 2022-05-31 08:40:16 +02:00
Ivan Enderlin bb8217d10f doc(crypto-nodejs) Disable missing docs for now. 2022-05-31 08:40:16 +02:00
Ivan Enderlin 6ff5c8e918 chore(crypto-js): Thanks Clippy. 2022-05-31 08:40:16 +02:00
Ivan Enderlin 7fa89f76aa chore(crypto-js): Update vodozemac's version to match matrix-sdk-crypto's. 2022-05-31 08:40:16 +02:00
Ivan Enderlin 0335fdd07f doc(crypto): Add missing module documentation. 2022-05-31 08:40:16 +02:00
Ivan Enderlin 3f8e3b61ff test(xtask) Replace WasmFeatureSet::MatrixSdkCrypto by *Js. 2022-05-31 08:40:16 +02:00
Ivan Enderlin efe5ea6a9c test(ci): Exclude matrix-sdk-crypto-(js|nodejs) from code coverage. 2022-05-31 08:40:16 +02:00
Ivan Enderlin 6afeeea56c test(ci): Do not compile matrix-sdk-crypto to Wasm, but -crypto-js instead. 2022-05-31 08:40:16 +02:00
Ivan Enderlin bef1dfbf79 chore(crypto-js): Fix typos. 2022-05-31 08:40:16 +02:00
Ivan Enderlin 51488b40d0 doc(crypto-js) Add missing documentation. 2022-05-31 08:40:16 +02:00
Ivan Enderlin b6a637893e chore(crypto-js): Fix cargo fmt. 2022-05-31 08:40:16 +02:00
Ivan Enderlin 2a4dce30b2 Merge branch 'main' into feat-crypto-wasm 2022-05-31 08:40:11 +02:00
Ivan Enderlin 3194ad1f9a feat(crypto-js): Implement OlmMachine.get_verification. 2022-05-31 08:39:34 +02:00
Ivan Enderlin ee648144a2 feat(crypto-nodejs): Split into a new crate: matrix-sdk-crypto-nodejs.
Why? Because `napi` and `wasm-bindgen` are too different. At this
step, the most notable bugs are the way `napi` is handling its proc
macros. There is too much conflicts when used with `#[cfg_attr]`. It
makes the code repetitive and harder to read and to understand (and
also to compile, we must be very careful). But on the short-term,
quickly, we will see more notable differences between `wasm-bindgen`
and `napi`, e.g. with array (in `wasm-bindgen`, we can downcast array
items into particular types, with `napi` it's going to be a very
different code).

Instead of fighting the proc macros bugs now, and having to split the
code later inside the same crate, we believe it's a good idea to split
the code now into 2 crates. At first we will see obvious code
duplications, but on the short-term, the code is likely to be more and
more different.
2022-05-31 08:39:34 +02:00
Ivan Enderlin afef9103a3 feat(crypto-js): Continue to port the API to NodeJS. 2022-05-31 08:39:34 +02:00
Ivan Enderlin 4df5ee6087 chore(crypto-js): Improve the Makefile for NodeJS support. 2022-05-31 08:39:34 +02:00
Ivan Enderlin 1a731ec385 feat(crypto-js): Rename OlmMachine.encrypt to .encrypt_room_event. 2022-05-31 08:39:34 +02:00
Ivan Enderlin 84fe78bab3 Merge branch 'main' into feat-crypto-wasm 2022-05-31 08:39:29 +02:00
Ivan Enderlin bce11b209e feat(crypto-js): Implement OlmMachine.update_tracked_users. 2022-05-31 08:38:24 +02:00
Ivan Enderlin 6c472f873f feat(crypto-js): Start adding support for NodeJS. 2022-05-31 08:38:24 +02:00
Ivan Enderlin 250b85dc79 feat(crypto): Extract js module to its own crate: matrix-sdk-crypto-js. 2022-05-31 08:38:13 +02:00
Ivan Enderlin f3ab1ae276 chore(crypto): Fix a typo. 2022-05-31 08:36:37 +02:00
Ivan Enderlin 6b2df7afdd feat(crypto): Implement `OlmMachine.get_missing_sessions. 2022-05-31 08:36:37 +02:00
Ivan Enderlin cdb252be5e chore(crypto): Move everything inside crates/matrix-sdk-crypto/. 2022-05-31 08:36:37 +02:00
Ivan Enderlin db30ef6ee4 test(crypto): Add tests for the Wasm API. 2022-05-31 08:36:37 +02:00
Ivan Enderlin da8699fe40 feat(crypto): DeviceLists.new expects Array<UserId>s now. 2022-05-31 08:36:37 +02:00
Ivan Enderlin 16b5eebe23 feat(crypto): Implement a hacky downcast function. 2022-05-31 08:36:37 +02:00
Ivan Enderlin 8161360852 feat(crypto): Add toString methods on identifier objects. 2022-05-31 08:36:37 +02:00
Ivan Enderlin f01cfe42b4 feat(crypto): Implement EncryptionSettings.new with default values. 2022-05-31 08:36:37 +02:00
Ivan Enderlin a024c9b268 docs(crypto): Add missing documentation. 2022-05-31 08:36:37 +02:00
Ivan Enderlin c20349f46f feat(crypto): Implement OlmMachine.share_group_session. 2022-05-31 08:36:37 +02:00
Ivan Enderlin e9d37d7c4e docs(crypto): Add missing docs. 2022-05-31 08:36:37 +02:00
Ivan Enderlin 8b94aed27f fix(crypto): Use txn_id for transaction_id. 2022-05-31 08:36:37 +02:00
Ivan Enderlin dc5b15e799 fix(crypto): OutgoingRequest::ToDeviceRequest was not correctly mapped. 2022-05-31 08:36:37 +02:00
Ivan Enderlin 30e9189f7b fix(crypto): Use JavaScript naming convention for OlmMachine.invalidate_group_session. 2022-05-31 08:36:37 +02:00
Ivan Enderlin b6a5d4962b Merge branch 'main' into feat-crypto-wasm 2022-05-31 08:36:30 +02:00
Ivan Enderlin ee713f928f feat(crypto): Implement OlmMachine.encrypt and .invalidate_group_session. 2022-05-31 08:35:09 +02:00
Ivan Enderlin 444ff9936b feat(crypto): Implement RoomId. 2022-05-31 08:35:09 +02:00
Ivan Enderlin 2451815226 feat(crypto): Implement *PublicKey.to_base64 and .length. 2022-05-31 08:35:09 +02:00
Ivan Enderlin 86c6e601bd chore(crypto): Refactor the code with TryFrom on a tuple. 2022-05-31 08:35:09 +02:00
Ivan Enderlin 9f159ff5a4 feat(crypto): Implement OlmMachine.mark_request_as_sent.
To implement this method, a new `responses::OwnedResponse`
intermediate type was needed. In addition to that, the `http` crate is
now required when the `js` feature is enabled.
2022-05-31 08:35:09 +02:00
Ivan Enderlin 056f34883f feat(crypto): Implement OlmMachine.trackedUsers. 2022-05-31 08:35:09 +02:00
Ivan Enderlin 2d15f758da feat(crypto): Use JavaScript naming style for methods. 2022-05-31 08:35:09 +02:00
Ivan Enderlin acd5de3cf3 feat(crypto): Implement ServerName, and add UserId.serverName. 2022-05-31 08:35:09 +02:00
Ivan Enderlin f0bb35a96c feat(crypto): Add changed and left to the constructor of DeviceLists. 2022-05-31 08:35:09 +02:00
Ivan Enderlin 1d38e54739 feat(crypto): Return a JsError rather than a String. 2022-05-31 08:35:09 +02:00
Ivan Enderlin 08665dcd1c feat(crypto): Implement our own future_to_promise helper to simplify code.
`wasm_bindgen_future::future_to_promise` expects a `Future<Output =
Result<JsValue, JsValue>>`. We reimplement this function by expecting
a `Future<Output = Result<T, anyhow::Error>>` where `T:
Into<JsValue>`. That way, we apply the type conversions to `JsValue`
inside this helper rather than in the call site. Additionally, all
errors are managed automatically without having to deal with `JsError`
or `JsValue`. It makes the code simpler to read and easier to write
from my point of view.
2022-05-31 08:35:09 +02:00
Ivan Enderlin 569adb7ceb feat(crypto) Implement OlmMachine.identity_keys. 2022-05-31 08:35:09 +02:00
Ivan Enderlin fd4dff79d4 feat(crypto) Implement OlmMachine.user_id, .device_id and .display_name. 2022-05-31 08:35:09 +02:00
Ivan Enderlin fdb9fe21c6 feat(crypto) Remove existing unwrap code. 2022-05-31 08:35:09 +02:00
Ivan Enderlin 7e5eec82c5 feat(crypto) Implement OlmMachine.outgoing_requests. 2022-05-31 08:35:09 +02:00
Ivan Enderlin 901715bcf3 chore(crypto) Generate a smaller Wasm module. 2022-05-31 08:35:09 +02:00
Ivan Enderlin 3aa46fa746 feat(crypto) Implement OlmMachine.receive_sync_changes & friends. 2022-05-31 08:35:09 +02:00
Ivan Enderlin 3318789a8b feat(crypto) Implement UserId, DeviceId and OlmMachine in Wasm for JS. 2022-05-31 08:35:09 +02:00
Ivan Enderlin 0fe0910fea feat(crypto) Reduce Wasm binary size by enabling LTO. 2022-05-31 08:35:09 +02:00
Damir Jelić a11633381b Merge branch 'poljar/borrow-signature-check-fix' 2022-05-30 12:54:35 +02:00
Benjamin Kampmann 2ff6497604 Merge pull request #715 from gnunicorn/gnunicorn/issue694
docs: configure docs.rs build to contain more features
2022-05-27 22:24:02 +02:00
Damir Jelić 7f87abf408 refactor(crypto): Simplify the signature checking method 2022-05-27 21:29:58 +02:00
Damir Jelić dd49a8bd43 fix(crypto): Don't require mutable borrows when signature checking 2022-05-27 21:28:35 +02:00
Damir Jelić f8c3a1d03b Merge branch 'poljar/signatures-struct' 2022-05-27 20:58:45 +02:00
Damir Jelić 808c4c6f3c chore(crypto): Remove some indentation levels in the types module
Co-authored-by: Jonas Platte <jplatte@element.io>
2022-05-27 20:55:17 +02:00
Damir Jelić a14549d5cc fix(crypto): Don't unwrap when converting to canonical json 2022-05-27 20:55:17 +02:00
Damir Jelić 9b5b4ab2b2 refactor(crypto): Use the Signatures struct for the CrossSigningKey 2022-05-27 20:55:17 +02:00
Damir Jelić 5756057719 refactor(crypto): Use the new Signatures struct for the DeviceKeys 2022-05-27 20:55:17 +02:00
Damir Jelić c5ef3e7c94 refactor(crypto): Turn the SignedKeySignatures into a struct 2022-05-27 20:55:17 +02:00
Damir Jelić fa7c1b60f6 feat(sdk): Replace some Mutex usage with a OnceCell
This makes some simpler getter methods non-async.
2022-05-27 16:37:51 +02:00
Julian Sparber 39b1fff218 Merge remote-tracking branch 'origin/main' into user_once_cell_for_session 2022-05-27 15:53:22 +02:00
Benjamin Kampmann c6df6b421c docs: fix target and feature-list for docsrs 2022-05-27 13:04:07 +02:00
Benjamin Kampmann f9b4fd2d12 docs: configure docs.rs build to contain more features 2022-05-27 10:44:16 +02:00
Damir Jelić e5390e18de refactor(crypto): Introduce a SignJson trait
This patch collects our json signing logic into a single place.
2022-05-27 10:39:12 +02:00
Brandon Lau 28cd0c19e9 fix(crypto-ffi): make crypto-ffi generate a static library for iOS clients 2022-05-27 10:38:21 +02:00
Damir Jelić 15ebc95cd0 chore(qrcode): Add more missing Eq implementations 2022-05-25 13:52:22 +02:00
Damir Jelić 39e12a86ca chore(crypto): Bump vodozemac 2022-05-25 13:49:00 +02:00
依云 ec9ec2567f feat: Add .resolve_room_alias() method to the Client 2022-05-24 08:43:02 +02:00
Damir Jelić fa9c91fb0e fix(crypto-ffi): Add support to encrypt custom events 2022-05-23 19:37:04 +02:00
Johannes Becker 24b71bf869 feat(appservice): Improve autojoin example 2022-05-23 14:14:05 +02:00
Benjamin Kampmann dd83cdd74b Merge pull request #704 from matrix-org/poljar/eq-store-encryption
feat(store-encryption): Derive Eq for some structs
2022-05-23 13:07:29 +02:00
Jonas Platte 453cae641b fix(docs): Remove history gh-pages branch
This should drastically reduce the repository size.
2022-05-23 12:57:45 +02:00
Damir Jelić f838b5fae8 fix(base): Fix a clippy warning about temporary variables 2022-05-23 11:35:23 +02:00
Damir Jelić 1f722f224f feat(crypto): Add a bunch of Eq implementations 2022-05-23 11:35:07 +02:00
Damir Jelić a992a4e831 feat(store-encryption): Derive Eq for some structs 2022-05-23 11:14:09 +02:00
Benjamin Kampmann 017359b081 Merge pull request #700 from gnunicorn/b-improve-coverage-reporting
Improve CI coverage reporting
2022-05-20 12:33:05 +02:00
Benjamin Kampmann 08718acd57 Merge pull request #698 from matrix-org/poljar/rename-encrypt-method
refactor(crypto): Make it clear that the encrypt method is for room events
2022-05-20 10:35:03 +02:00
Benjamin Kampmann dd1f817701 ci(CodeCoverage): exclude ffi & wasm from default project 2022-05-20 10:26:31 +02:00
Benjamin Kampmann 81db9ef4ca ci(CodeCoverage): Remove patch-level coverage reporting 2022-05-20 08:34:56 +02:00
Benjamin Kampmann e927834108 ci(CodeCoverage): split code coverage reporting in mandatory and optional areas 2022-05-20 08:34:39 +02:00
Damir Jelić f3045dbf99 fixup! refactor(crypto): Make it clear that the encrypt method is for room events 2022-05-20 08:32:50 +02:00
Jonas Platte d6c6211c00 chore: Use matrix_sdk::Result alias more 2022-05-20 07:52:39 +02:00
Jonas Platte 85949081ce chore: Consistently use anyhow for Result-returning doctests
Cuts down on syntactic noise.
2022-05-20 07:52:39 +02:00
Jonas Platte cbcc5feef2 fix(sled): Fix unused import when experimental-timeline isn't enabled 2022-05-20 07:52:39 +02:00
Jonas Platte 1b569a8fd4 chore: Consistently use anyhow::Result for example main fn's
When an `Err` is propagated out of `main`, it will be printed using
`Debug`, which is much easier to read in anyhow::Error's case. See also
https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations
2022-05-20 07:52:39 +02:00
Damir Jelić 813f388812 refactor(crypto): Make it clear that the encrypt method is for room events 2022-05-20 07:47:47 +02:00
Damir Jelić 552de33dbc Merge branch 'dkasak/docs-improvements' 2022-05-20 07:26:00 +02:00
Denis Kasak c3f2003eb7 docs: Slightly reword CryptoStore doc for consistency. 2022-05-19 16:22:09 +02:00
Denis Kasak 3b70d7f9ba docs: Improve wording of store module-level comment
Includes a fix to refer to the `e2e-encryption` feature instead of
`encryption` (which doesn't exist).
2022-05-19 16:22:09 +02:00
Denis Kasak c00c9d7b81 docs: Reword feature table so the wording is more uniform 2022-05-19 16:22:09 +02:00
Denis Kasak 126e8f1bd9 docs: Unify references to "cryptostore" as "crypto store" 2022-05-19 16:22:09 +02:00
Denis Kasak 9e6e76e5ed docs: Fix reference to ClientConfig in doc comment 2022-05-19 15:38:13 +02:00
Damir Jelić d1410fcded ci: Run cargo audit every day 2022-05-19 10:03:46 +02:00
Jonas Platte b955e7aad9 Use anyhow::Ok instead of turbofish on Result 2022-05-18 22:04:44 +02:00
Jonas Platte a364018c0e chore: Use serde::de::DeserializeOwned convenience trait 2022-05-18 22:04:44 +02:00
Jonas Platte 0f910b6229 fix(base): Make max_power_level reflect the current maximum only
… instead of taking into account an older maximum as well.
2022-05-18 14:03:12 +02:00
Benjamin Kampmann 736f0e7687 feat: Initial apple platforms support
Merge pull request #571 from matrix-org/jplatte/matrix-sdk-ffi
2022-05-18 13:14:29 +02:00
Stefan Ceriu 6a1b85c560 Fix xcframework debug build script 2022-05-18 13:43:29 +03:00
Benjamin Kampmann e3503fe102 ci: one more move 2022-05-18 11:56:38 +02:00
Benjamin Kampmann 4522b4e8f5 ci: move rust cache to include uniffi bindgen install 2022-05-18 11:32:03 +02:00
Benjamin Kampmann 886809b579 chore(Apple): Move apple into subfolder 2022-05-18 11:24:30 +02:00
Benjamin Kampmann 97b91a47f1 ci: Setup CI test runs FFI crate (Apple platforms support)
Merge pull request #679 from matrix-org/stefan/matrix-sdk-ffi
2022-05-18 10:52:39 +02:00
Stefan Ceriu 4b8b9db075 Have FII action only run on PRs targetting main, similar to the CI one. 2022-05-18 11:49:29 +03:00
Jonas Platte bc207f1e5c chore(sdk): Upgrade async-once-cell to 0.3.1 2022-05-18 10:31:52 +02:00
Stefan Ceriu d863b4eb75 Bump uniffi to version 0.18.0 2022-05-18 11:13:15 +03:00
Stefan Ceriu 45cb162e5a Fix sample project, cleanup tests and add github action 2022-05-18 11:08:51 +03:00
Stefan Ceriu ba2cef62b0 Move apple scripts; tweak the debug one to allow for CI builds 2022-05-18 11:08:51 +03:00
Jonas Platte 2d7fb1b61c fix(matrix-sdk): room::Common:messages() don't return decryption error
If decryption fails we want that the user still has access to
the events.
This was broken in a previous commit.
2022-05-17 18:09:09 +02:00
Julian Sparber d6f9e5f6b6 Remove extra controll flow 2022-05-17 17:52:12 +02:00
Julian Sparber 9c07ff1166 matrix-sdk: room::Common:messages() don't return decryption error
If decryption fails we want that the user still has access to
the events.
This was broken in a previous commit.
2022-05-17 17:23:32 +02:00
Julian Sparber 5bd7d17234 fix(matrix-sdk): room::Common:event() don't return decryption error 2022-05-17 17:03:28 +02:00
Benjamin Kampmann 7d439b1697 Merge pull request #680 from gnunicorn/ben-fix-docs
Fixing docs
2022-05-17 15:45:39 +02:00
Benjamin Kampmann 39f1b9d464 docs: fix link
Co-authored-by: Jonas Platte <jplatte+git@posteo.de>
2022-05-17 13:54:37 +02:00
Benjamin Kampmann 8a86369e68 Merge branch 'ben-fix-docs' into jplatte/matrix-sdk-ffi 2022-05-17 13:39:46 +02:00
Benjamin Kampmann a19999b240 docs: fixing broken inner link 2022-05-17 13:38:07 +02:00
Benjamin Kampmann a72a05edc3 Merge remote-tracking branch 'origin/main' into jplatte/matrix-sdk-ffi 2022-05-17 13:05:24 +02:00
Julian Sparber d0cc3d9c2b Don't split links to two lines
Co-authored-by: Jonas Platte <jplatte+git@posteo.de>
2022-05-17 12:56:24 +02:00
Benjamin Kampmann 2862934b87 style: fix clippy lints for ffi 2022-05-17 12:51:47 +02:00
Julian Sparber 3f3f9d653b Fix code style 2022-05-17 12:46:06 +02:00
Julian Sparber 83fc91f87e Add a pretty label for Session link 2022-05-17 12:39:11 +02:00
Julian Sparber 1e77c39498 Fix store example 2022-05-17 12:37:51 +02:00
Julian Sparber 1de27bcc0c Fix CI and address requested changes 2022-05-17 12:22:35 +02:00
Amanda Graven 248fff370a test(base): Remove invalid sync event from json
The test json used for mocking a sync response contained an ill-formed
event with a room_id key present in the event. Since the deserialization
ignores the room_id key, this resulted in the client's membership state
evaluating to left going by the contents of the state events, despite
the room being in the "joined" section of the sync response. This is a
violation of the spec.
2022-05-17 11:31:36 +02:00
Julian Sparber 26dd2d0e62 Merge remote-tracking branch 'origin/main' into user_once_cell_for_session 2022-05-17 11:14:28 +02:00
Benjamin Kampmann 4bbfa4345f Merge pull request #678 from Hywan/fix-crypto-doc
docs(crypto): Remove outdated documentation and use auto-link
2022-05-17 10:28:07 +02:00
Johannes Becker 5f92113627 chore: Use resolver2 for workspace 2022-05-17 10:24:28 +02:00
Ivan Enderlin 426a93f07c docs(crypto): Remove outdated documentation and use auto-link. 2022-05-17 10:08:42 +02:00
Benjamin Kampmann 0949979a1b Merge remote-tracking branch 'origin/main' into jplatte/matrix-sdk-ffi 2022-05-16 13:35:51 +02:00
Benjamin Kampmann 76144de881 Fix(SDK): add missing import for experimental-timeline feature
Merge pull request #671 from jsparber/fix_import
2022-05-13 10:33:03 +02:00
Stefan Ceriu bd4763235a chore(matrix-sdk-ffi) Use stable toolchain for targeting aarch64-apple-ios-sim 2022-05-12 16:49:49 +03:00
Julian Sparber 911b5415b9 ci: Use experimental-timeline feature 2022-05-12 13:59:18 +02:00
Julian Sparber c3fc6ff58f Fix missing import for experimental-timeline feature 2022-05-12 12:38:58 +02:00
Benjamin Kampmann 4c84f252d2 Merge pull request #667 from matrix-org/ben-releasing-base-0.5.1
Release matrix-sdk-base 0.5.1
2022-05-11 19:23:16 +02:00
Benjamin Kampmann 70c0626882 chore: tag base 0.5.1 2022-05-11 19:01:12 +02:00
Benjamin Kampmann 5570181bf8 fix(sdk): Fix regression with push rules being applied to the own user_id only instead of all but the own user_id
Merge pull request #664 from JCWasmx86/ignore_notification
2022-05-11 18:52:55 +02:00
Benjamin Kampmann 7088ff89b5 Merge pull request #665 from matrix-org/ben-releasing-0.5.0
Releasing 0.5.0
2022-05-11 18:50:34 +02:00
JCWasmx86 3f36528a98 fix(sdk): Fix regression 2022-05-11 18:15:33 +02:00
Jonas Platte 1fdd1ab23a Cleanup after rebase 2022-05-11 11:57:27 +02:00
Jonas Platte 40dcc988d6 fixup! feat: Initial apple platforms support 2022-05-11 11:07:50 +02:00
Jonas Platte 9909cb597a squash! feat: Initial apple platforms support
Enable sending of messages
2022-05-11 11:07:50 +02:00
Jonas Platte 56cfe8f231 squash! feat: Initial apple platforms support
Some FFI API cleanup
2022-05-11 11:07:50 +02:00
Jonas Platte ab9d8eb52b feat: Initial apple platforms support
Co-authored-by: Stefan Ceriu <stefan.ceriu@gmail.com>
Co-authored-by: Benjamin Kampmann <ben.kampmann@gmail.com>
2022-05-11 11:07:42 +02:00
Julian Sparber c8233b6c85 Drop CryptoHolder and use OnceCell for OlmMachine
THe CryptoHolder is just used to hold the crypto-store till the
matrix_sdk::Session is set and it is possible to create the OlmMachine.
But with this approch we need to use a Mutex and getting the OlmMachine
becomes more expensive because of this. Therefore this replaces the
Mutex with a OnceCell. The only downside is that the BaseClient needs to
hold onto a referance to the crypto-store. (It would be possible to drop
it after the OlmMachine is created, but I don't think gives use any
improvment)
2022-05-11 10:48:12 +02:00
Julian Sparber b72bdcd7d4 Use OnceCell to store matrix_sdk::Session
Since the session can be set only once there is no point in using a
Mutex or RwLock.
2022-05-10 17:10:34 +02:00
Ivan Enderlin 5c6a6464c4 chore(crypto) Fix a typo in the code. 2022-05-09 11:50:40 +02:00
Ivan Enderlin d3c20b2a13 feat(crypto) Generate a cdylib for the crate.
Ask `rustc` to generate a dynamic system library, which will be useful
to generate a Wasm module.
2022-05-09 10:51:18 +02:00
Ivan Enderlin 7817af9aa6 feat(crypto) Add the js feature.
This patch updates to code to raise a compilation error if the `js`
feature is used for another architecture than `wasm32`.
2022-05-09 10:40:32 +02:00
Ivan Enderlin 7569c08ada feat(crypto) Add wasm-bindgen as a dep and simplify Cargo.toml. 2022-05-09 10:40:13 +02:00
476 changed files with 83567 additions and 22401 deletions
+17
View File
@@ -1,5 +1,16 @@
# Pass the rustflags specified to host dependencies (build scripts, proc-macros)
# when a `--target` is passed to Cargo. Historically this was not the case, and
# because of that, cross-compilation would not set the rustflags configured
# below in `target.'cfg(all())'` for them, resulting in cache invalidation.
#
# Since this is an unstable feature (enabled at the bottom of the file), this
# setting is unfortunately ignored on stable toolchains, but it's still better
# to have it apply on nightly than using the old behavior for all toolchains.
target-applies-to-host = false
[alias]
xtask = "run --package xtask --"
uniffi-bindgen = "run --package uniffi-bindgen --"
[doc.extern-map.registries]
crates-io = "https://docs.rs/"
@@ -23,3 +34,9 @@ rustflags = [
"-Wclippy::str_to_string",
"-Wclippy::todo",
]
# activate the target-applies-to-host feature.
# Required for `target-applies-to-host` at the top to take effect.
[unstable]
rustdoc-map = true
target-applies-to-host = true
+5
View File
@@ -0,0 +1,5 @@
[profile.default]
retries = 2
# kill the slow tests if they still aren't up after 180s
slow-timeout = { period = "60s", terminate-after = 3 }
+6
View File
@@ -0,0 +1,6 @@
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
+7
View File
@@ -0,0 +1,7 @@
<!-- description of the changes in this PR -->
- [ ] Public API changes documented in changelogs (optional)
<!-- Sign-off, if not part of the commits -->
<!-- See CONTRIBUTING.md if you don't know what this is -->
Signed-off-by:
-46
View File
@@ -1,46 +0,0 @@
name: Appservice
on:
push:
branches: [main]
pull_request:
branches: [main]
types:
- opened
- reopened
- synchronize
- ready_for_review
env:
CARGO_TERM_COLOR: always
jobs:
test-appservice:
if: github.event_name == 'push' || !github.event.pull_request.draft
name: ${{ matrix.os }} / appservice / stable
runs-on: ${{ matrix.os }}-latest
strategy:
fail-fast: true
matrix:
os: [ubuntu, macOS]
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Load cache
uses: Swatinem/rust-cache@v1
- name: Run checks
uses: actions-rs/cargo@v1
with:
command: run
args: -p xtask -- ci test-appservice
+13
View File
@@ -0,0 +1,13 @@
name: Security audit
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
+2 -5
View File
@@ -12,15 +12,12 @@ jobs:
steps:
- name: Checkout the repo
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install Rust
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@nightly
with:
toolchain: nightly
components: rustfmt
profile: minimal
override: true
- name: Run Benchmarks
run: cargo bench | tee benchmark-output.txt
+236
View File
@@ -0,0 +1,236 @@
name: Bindings tests
on:
workflow_dispatch:
push:
branches: [main]
pull_request:
branches: [main]
types:
- opened
- reopened
- synchronize
- ready_for_review
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
MATRIX_SDK_CRYPTO_NODEJS_PATH: bindings/matrix-sdk-crypto-nodejs
MATRIX_SDK_CRYPTO_JS_PATH: bindings/matrix-sdk-crypto-js
jobs:
xtask:
uses: ./.github/workflows/xtask.yml
test-uniffi-codegen:
name: Test UniFFI bindings generation
needs: xtask
if: github.event_name == 'push' || !github.event.pull_request.draft
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Protoc
uses: arduino/setup-protoc@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Load cache
uses: Swatinem/rust-cache@v2
- name: Get xtask
uses: actions/cache/restore@v3
with:
path: target/debug/xtask
key: "${{ needs.xtask.outputs.cachekey-linux }}"
fail-on-cache-miss: true
- name: Build library & generate bindings
run: target/debug/xtask ci bindings
lint-js-bindings:
strategy:
fail-fast: true
matrix:
include:
- name: "[m]-crypto-nodejs"
path: "bindings/matrix-sdk-crypto-nodejs"
- name: "[m]-crypto-js"
path: "bindings/matrix-sdk-crypto-js"
name: lint ${{ matrix.name }}
runs-on: ubuntu-latest
steps:
- name: Checkout the repo
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
- name: Install NPM dependencies
working-directory: ${{ matrix.path }}
run: npm install
- name: run lint
working-directory: ${{ matrix.path }}
run: npm run lint
test-matrix-sdk-crypto-nodejs:
name: ${{ matrix.os-name }} [m]-crypto-nodejs, v${{ matrix.node-version }}
if: github.event_name == 'push' || !github.event.pull_request.draft
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
# Use Ubuntu LTS-1 for broader glibc compatibility.
os: [ubuntu-20.04]
node-version: [16.0, 18.0, 19.0, 20.0]
include:
- os: ubuntu-20.04
os-name: 🐧
- os: macos-latest
os-name: 🍏
node-version: 20.0
- node-version: 20.0
build-doc: true
steps:
- name: Checkout the repo
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Load cache
uses: Swatinem/rust-cache@v2
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install NPM dependencies
working-directory: ${{ env.MATRIX_SDK_CRYPTO_NODEJS_PATH }}
run: npm install
- name: Build the Node.js binding
working-directory: ${{ env.MATRIX_SDK_CRYPTO_NODEJS_PATH }}
run: npm run release-build
- name: Test the Node.js binding
working-directory: ${{ env.MATRIX_SDK_CRYPTO_NODEJS_PATH }}
run: npm run test
# Building in dev-mode and copy lib in failure case
- name: Build the Node.js binding in non-release
if: failure()
working-directory: ${{ env.MATRIX_SDK_CRYPTO_NODEJS_PATH }}
run: |
cp *.node release-mode-lib.node
npm run build
- uses: actions/upload-artifact@v3
if: failure()
with:
name: Failure Files
path: |
bindings/matrix-sdk-crypto-nodejs/*.node
/var/crash/*.crash
- if: ${{ matrix.build-doc }}
name: Build the documentation
working-directory: ${{ env.MATRIX_SDK_CRYPTO_NODEJS_PATH }}
run: npm run doc
test-matrix-sdk-crypto-js:
name: 🕸 [m]-crypto-js
if: github.event_name == 'push' || !github.event.pull_request.draft
runs-on: ubuntu-latest
steps:
- name: Checkout the repo
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- name: Load cache
uses: Swatinem/rust-cache@v2
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 20.0
- name: Install NPM dependencies
working-directory: ${{ env.MATRIX_SDK_CRYPTO_JS_PATH }}
run: npm install
- name: Build the WebAssembly + JavaScript binding
working-directory: ${{ env.MATRIX_SDK_CRYPTO_JS_PATH }}
run: npm run build
- name: Test the JavaScript binding
working-directory: ${{ env.MATRIX_SDK_CRYPTO_JS_PATH }}
run: npm run test
- name: Build the documentation
working-directory: ${{ env.MATRIX_SDK_CRYPTO_JS_PATH }}
run: npm run doc
test-apple:
name: matrix-rust-components-swift
needs: xtask
runs-on: macos-12
if: github.event_name == 'push' || !github.event.pull_request.draft
steps:
- name: Checkout
uses: actions/checkout@v3
# install protoc in case we end up rebuilding opentelemetry-proto
- name: Install Protoc
uses: arduino/setup-protoc@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install aarch64-apple-ios target
run: rustup target install aarch64-apple-ios
- name: Load cache
uses: Swatinem/rust-cache@v2
- name: Get xtask
uses: actions/cache/restore@v3
with:
path: target/debug/xtask
key: "${{ needs.xtask.outputs.cachekey-macos }}"
fail-on-cache-miss: true
- name: Build library & bindings
run: target/debug/xtask swift build-library
- name: Run XCTests
working-directory: bindings/apple
run: swift test
- name: Build Framework
run: target/debug/xtask swift build-framework --only-target=aarch64-apple-ios
+412 -51
View File
@@ -1,4 +1,4 @@
name: CI
name: Rust tests
on:
workflow_dispatch:
@@ -12,12 +12,20 @@ on:
- synchronize
- ready_for_review
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
jobs:
test-features:
name: linux / features-${{ matrix.name }}
xtask:
uses: ./.github/workflows/xtask.yml
test-matrix-sdk-features:
name: 🐧 [m], ${{ matrix.name }}
needs: xtask
if: github.event_name == 'push' || !github.event.pull_request.draft
runs-on: ubuntu-latest
@@ -36,86 +44,439 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v3
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
uses: dtolnay/rust-toolchain@stable
- name: Load cache
uses: Swatinem/rust-cache@v1
uses: Swatinem/rust-cache@v2
with:
# use a separate cache for each job to work around
# https://github.com/Swatinem/rust-cache/issues/124
key: "${{ matrix.name }}"
# ... but only save the cache on the main branch
# cf https://github.com/Swatinem/rust-cache/issues/95
save-if: ${{ github.ref == 'refs/head/main' }}
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Get xtask
uses: actions/cache/restore@v3
with:
path: target/debug/xtask
key: "${{ needs.xtask.outputs.cachekey-linux }}"
fail-on-cache-miss: true
- name: Test
uses: actions-rs/cargo@v1
with:
command: run
args: -p xtask -- ci test-features ${{ matrix.name }}
run: |
target/debug/xtask ci test-features ${{ matrix.name }}
test-crypto-features:
name: linux / crypto-crate features
test-matrix-sdk-examples:
name: 🐧 [m]-examples
needs: xtask
runs-on: ubuntu-latest
if: github.event_name == 'push' || !github.event.pull_request.draft
steps:
- name: Checkout the repo
uses: actions/checkout@v2
- name: Checkout the repo
uses: actions/checkout@v3
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Load cache
uses: Swatinem/rust-cache@v1
- name: Load cache
uses: Swatinem/rust-cache@v2
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: run
args: -p xtask -- ci test-crypto
- name: Install nextest
uses: taiki-e/install-action@nextest
test:
- name: Get xtask
uses: actions/cache/restore@v3
with:
path: target/debug/xtask
key: "${{ needs.xtask.outputs.cachekey-linux }}"
fail-on-cache-miss: true
- name: Test
run: |
target/debug/xtask ci examples
test-matrix-sdk-crypto:
name: 🐧 [m]-crypto
needs: xtask
runs-on: ubuntu-latest
if: github.event_name == 'push' || !github.event.pull_request.draft
steps:
- name: Checkout the repo
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Load cache
uses: Swatinem/rust-cache@v2
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Get xtask
uses: actions/cache/restore@v3
with:
path: target/debug/xtask
key: "${{ needs.xtask.outputs.cachekey-linux }}"
fail-on-cache-miss: true
- name: Test
run: |
target/debug/xtask ci test-crypto
test-all-crates:
name: ${{ matrix.name }}
if: github.event_name == 'push' || !github.event.pull_request.draft
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
name:
- linux / stable
- linux / beta
- macOS / stable
include:
- name: linux / stable
- name: 🐧 all crates, 🦀 stable
rust: stable
os: ubuntu-latest
- name: linux / beta
- name: 🐧 all crates, 🦀 beta
rust: beta
os: ubuntu-latest
- name: macOS / stable
os: macOS-latest
- name: 🍏 all crates, 🦀 stable
rust: stable
os: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v3
- name: Install Rust
uses: actions-rs/toolchain@v1
- name: Install Protoc
uses: arduino/setup-protoc@v1
with:
toolchain: ${{ matrix.rust || 'stable' }}
profile: minimal
override: true
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Rust stable
if: matrix.rust == 'stable'
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- name: Load cache
uses: Swatinem/rust-cache@v1
uses: Swatinem/rust-cache@v2
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Test
uses: actions-rs/cargo@v1
run: |
cargo nextest run --workspace \
--exclude matrix-sdk-integration-testing --exclude sliding-sync-integration-test
- name: Test documentation
run: |
cargo test --doc --features docsrs
test-wasm:
name: 🕸️ ${{ matrix.name }}
needs: xtask
if: github.event_name == 'push' || !github.event.pull_request.draft
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
include:
- name: '[m]-qrcode'
cmd: matrix-sdk-qrcode
- name: '[m]-base'
cmd: matrix-sdk-base
- name: '[m]-common'
cmd: matrix-sdk-common
- name: '[m]-indexeddb, no crypto'
cmd: indexeddb-no-crypto
- name: '[m]-indexeddb, with crypto'
cmd: indexeddb-with-crypto
- name: '[m], no-default, wasm-flags'
cmd: matrix-sdk-no-default
- name: '[m], indexeddb stores'
cmd: matrix-sdk-indexeddb-stores
- name: '[m], indexeddb stores, no crypto'
cmd: matrix-sdk-indexeddb-stores-no-crypto
- name: '[m], wasm-example'
cmd: matrix-sdk-command-bot
steps:
- name: Checkout the repo
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
command: test
targets: wasm32-unknown-unknown
components: clippy
- name: Install wasm-pack
uses: jetli/wasm-pack-action@v0.4.0
with:
version: v0.10.3
- name: Load cache
uses: Swatinem/rust-cache@v2
with:
# use a separate cache for each job to work around
# https://github.com/Swatinem/rust-cache/issues/124
key: "${{ matrix.cmd }}"
# ... but only save the cache on the main branch
# cf https://github.com/Swatinem/rust-cache/issues/95
save-if: ${{ github.ref == 'refs/head/main' }}
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Get xtask
uses: actions/cache/restore@v3
with:
path: target/debug/xtask
key: "${{ needs.xtask.outputs.cachekey-linux }}"
fail-on-cache-miss: true
- name: Rust Check
run: |
target/debug/xtask ci wasm ${{ matrix.cmd }}
- name: Wasm-Pack test
run: |
target/debug/xtask ci wasm-pack ${{ matrix.cmd }}
test-appservice:
name: ${{ matrix.os-name }} [m]-appservice
needs: xtask
if: github.event_name == 'push' || !github.event.pull_request.draft
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
include:
- os: ubuntu-latest
os-name: 🐧
xtask-cachekey: "${{ needs.xtask.outputs.cachekey-linux }}"
- os: macos-latest
os-name: 🍏
xtask-cachekey: "${{ needs.xtask.outputs.cachekey-macos }}"
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Load cache
uses: Swatinem/rust-cache@v2
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Get xtask
uses: actions/cache/restore@v3
with:
path: target/debug/xtask
key: "${{ matrix.xtask-cachekey }}"
fail-on-cache-miss: true
- name: Run checks
run: |
target/debug/xtask ci test-appservice
formatting:
name: Check Formatting
runs-on: ubuntu-latest
if: github.event_name == 'push' || !github.event.pull_request.draft
steps:
- name: Checkout the repo
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt
- name: Cargo fmt
run: |
cargo fmt -- --check
typos:
name: Spell Check with Typos
runs-on: ubuntu-latest
if: github.event_name == 'push' || !github.event.pull_request.draft
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Check the spelling of the files in our repo
uses: crate-ci/typos@v1.13.0
clippy:
name: Run clippy
needs: xtask
runs-on: ubuntu-latest
if: github.event_name == 'push' || !github.event.pull_request.draft
steps:
- name: Checkout the repo
uses: actions/checkout@v3
- name: Install Protoc
uses: arduino/setup-protoc@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Rust
uses: dtolnay/rust-toolchain@nightly
with:
components: clippy
- name: Load cache
uses: Swatinem/rust-cache@v2
- name: Get xtask
uses: actions/cache/restore@v3
with:
path: target/debug/xtask
key: "${{ needs.xtask.outputs.cachekey-linux }}"
fail-on-cache-miss: true
- name: Clippy
run: |
target/debug/xtask ci clippy
integration-tests:
name: Integration test
if: github.event_name == 'push' || !github.event.pull_request.draft
runs-on: ubuntu-latest
steps:
- name: Checkout the repo
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Load cache
uses: Swatinem/rust-cache@v2
- name: Install nextest
uses: taiki-e/install-action@nextest
- uses: actions/setup-python@v4
with:
python-version: 3.8
- uses: michaelkaye/setup-matrix-synapse@main
with:
uploadLogs: true
httpPort: 8228
disableRateLimiting: true
- name: Test
run: |
cargo nextest run -p matrix-sdk-integration-testing
sliding-sync-integration-tests:
name: Sliding Sync Integration test
if: github.event_name == 'push' || !github.event.pull_request.draft
runs-on: ubuntu-latest
# run several docker containers with the same networking stack so the hostname 'postgres'
# maps to the postgres container, etc.
services:
# sliding sync needs a postgres container
postgres:
# Docker Hub image
image: postgres
# Provide the password for postgres
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: syncv3
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
# run sliding sync and point it at the postgres container and synapse container.
# the postgres container needs to be above this to make sure it has started prior to this service.
slidingsync:
image: "ghcr.io/matrix-org/sliding-sync:v0.99.0"
env:
SYNCV3_SERVER: "http://synapse:8008"
SYNCV3_SECRET: "SUPER_CI_SECRET"
SYNCV3_BINDADDR: ":8118"
SYNCV3_DB: "user=postgres password=postgres dbname=syncv3 sslmode=disable host=postgres"
ports:
- 8118:8118
# tests need a synapse: this is a service and not michaelkaye/setup-matrix-synapse@main as the
# latter does not provide networking for services to communicate with it.
synapse:
# Custom image built from https://github.com/matrix-org/synapse/tree/v1.72.0/docker/complement
# with a dummy /complement/ca set
image: ghcr.io/matrix-org/synapse-service:v1.72.0
env:
SYNAPSE_COMPLEMENT_DATABASE: sqlite
SERVER_NAME: synapse
ports:
- 8008:8008
steps:
- name: Checkout the repo
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Load cache
uses: Swatinem/rust-cache@v2
- name: Install nextest
uses: taiki-e/install-action@nextest
- uses: actions/setup-python@v4
with:
python-version: 3.8
- name: Test
env:
RUST_LOG: "hyper=trace"
HOMESERVER_URL: "http://localhost:8008"
HOMESERVER_DOMAIN: "synapse"
SLIDING_SYNC_PROXY_URL: "http://localhost:8118"
run: |
cargo nextest run -p sliding-sync-integration-test
+33 -14
View File
@@ -6,6 +6,10 @@ on:
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
@@ -13,32 +17,47 @@ jobs:
code_coverage:
name: Code Coverage
runs-on: "ubuntu-latest"
if: github.event_name == 'push' || !github.event.pull_request.draft
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
uses: dtolnay/rust-toolchain@stable
- name: Load cache
uses: Swatinem/rust-cache@v1
uses: Swatinem/rust-cache@v2
- name: Install tarpaulin
uses: actions-rs/cargo@v1
uses: taiki-e/install-action@v2
with:
command: install
args: cargo-tarpaulin
tool: cargo-tarpaulin
# set up backend for integration tests
- uses: actions/setup-python@v4
with:
python-version: 3.8
- uses: gnunicorn/setup-matrix-synapse@main
with:
uploadLogs: true
httpPort: 8228
disableRateLimiting: true
serverName: "matrix-sdk.rs"
- name: Run tarpaulin
uses: actions-rs/cargo@v1
with:
command: tarpaulin
args: --ignore-config --exclude-files "crates/matrix-sdk/examples/*,crates/matrix-sdk-common,crates/matrix-sdk-test" --out Xml
run: |
cargo tarpaulin --out Xml -e sliding-sync-integration-test
- name: Upload to codecov.io
uses: codecov/codecov-action@v3
with:
# Work around frequent upload errors, for runs inside the main repo (not PRs from forks).
# Otherwise not required for public repos.
token: ${{ secrets.CODECOV_UPLOAD_TOKEN }}
# The upload sometimes fails due to https://github.com/codecov/codecov-action/issues/837.
# To make sure that the failure gets flagged clearly in the UI, fail the action.
fail_ci_if_error: true
-42
View File
@@ -1,42 +0,0 @@
name: Docs
on:
push:
branches: [main]
pull_request:
jobs:
docs:
name: Docs
runs-on: ubuntu-latest
if: github.event_name == 'push' || !github.event.pull_request.draft
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
- name: Load cache
uses: Swatinem/rust-cache@v1
# Keep in sync with xtask docs
- name: Build docs
uses: actions-rs/cargo@v1
env:
RUSTDOCFLAGS: "--enable-index-page -Zunstable-options --cfg docsrs -Dwarnings"
with:
command: doc
args: --no-deps --workspace --features docsrs -Zrustdoc-map
- name: Deploy docs
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./target/doc/
+69
View File
@@ -0,0 +1,69 @@
name: Documentation
on:
push:
branches: [main]
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
docs:
name: All crates
runs-on: ubuntu-latest
if: github.event_name == 'push' || !github.event.pull_request.draft
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@nightly
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 20
- name: Load cache
uses: Swatinem/rust-cache@v2
# Keep in sync with xtask docs
- name: Build rust documentation
env:
# Work around https://github.com/rust-lang/cargo/issues/10744
CARGO_TARGET_APPLIES_TO_HOST: "true"
RUSTDOCFLAGS: "--enable-index-page -Zunstable-options --cfg docsrs -Dwarnings"
run:
cargo doc --no-deps --features docsrs
- name: Build `matrix-sdk-crypto-nodejs` doc
run: |
cd bindings/matrix-sdk-crypto-nodejs
npm install
npm run build && npm run doc
- name: Build `matrix-sdk-crypto-js` doc
run: |
cd bindings/matrix-sdk-crypto-js
npm install
npm run build && npm run doc
- name: Prepare the doc hierarchy
shell: bash
run: |
mkdir -p doc/bindings/matrix-sdk-crypto-nodejs/
mkdir -p doc/bindings/matrix-sdk-crypto-js/
mv target/doc/* doc/
mv bindings/matrix-sdk-crypto-nodejs/docs/* doc/bindings/matrix-sdk-crypto-nodejs/
mv bindings/matrix-sdk-crypto-js/docs/* doc/bindings/matrix-sdk-crypto-js/
- name: Deploy documentation
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./doc/
force_orphan: true
@@ -0,0 +1,119 @@
name: Prepare Crypto-Node.js Release
#
# This is a helper workflow to craft a new Node.js release, trigger this via
# the Github Workflow UI by dispatching it manually. Provide the version, the
# matrix-sdk-crypto-nodejs npm package should be set to, and a optionally the
# old version (as used in the git tag) this release should be compared to.
#
# This will then:
# 1. bump the npm version to the one you specified
# 2. commit that change together with the changelog (if it changed, see below)
# 3. create the appropriate tag on that commit
# 4. create the Github draft release, including the changes (if given, see below)
# 5. push these to a new branch, including tag, triggering the `release-crypto-nodejs` workflow
# 6. create a PR to merge these back into `main`
#
# Additionally, if you provide a tag to comapare this tag to, this will:
# 1. create a changelog between the two releases, used for the github release
# 2. update the Changelog.md and include it in the commit
#
# The remaining tasks are done by the release-crypto-nodejs workflow.
on:
workflow_dispatch:
inputs:
version:
description: 'New Node.js SemVer version to create'
required: true
type: string
previous_version:
description: 'Create the changelog by comparing to this old SemVer Version (as used in the tag) '
type: string
env:
PKG_PATH: "bindings/matrix-sdk-crypto-nodejs"
TAG_PREFIX: "matrix-sdk-crypto-nodejs-v"
jobs:
prepare-release:
name: "Preparing crypto-nodejs release tag"
runs-on: ubuntu-latest
outputs:
tag: "${{ env.TAG_PREFIX }}${{ inputs.version }}"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
# Generate changelog since last tag, if given
- name: Generate a changelog for upload
if: inputs.previous_version
uses: orhun/git-cliff-action@v1
with:
config: "${{ env.PKG_PATH }}/cliff.toml"
args: --strip header "${{env.TAG_PREFIX}}${{ inputs.previous_version }}..HEAD"
env:
GIT_CLIFF_TAG: "Changes ${{ inputs.previous_version }} -> ${{ inputs.version }}"
GIT_CLIFF_OUTPUT: "${{ env.PKG_PATH }}/CHANGES-${{ inputs.version }}.md"
# Update changelog since last tag, if given
- name: Update existing Changelog
if: inputs.previous_version
uses: orhun/git-cliff-action@v1
with:
config: "${{ env.PKG_PATH }}/cliff.toml"
args: "${{env.TAG_PREFIX}}${{ inputs.previous_version }}..HEAD"
env:
GIT_CLIFF_TAG: "${{ inputs.version }}"
GIT_CLIFF_PREPEND: "${{ env.PKG_PATH }}/CHANGELOG.md"
- name: Set version
id: package_version
working-directory: ${{ env.PKG_PATH }}
run: npm version ${{ inputs.version }}
- uses: EndBug/add-and-commit@v9
with:
default_author: github_actions
message: "Tagging Crypto-Node.js for release"
tag: "${{env.TAG_PREFIX}}${{ inputs.version }}"
new_branch: "gh-action/release-${{ env.TAG_PREFIX }}${{ inputs.version }}"
push: true
add: |
${{ env.PKG_PATH }}/package.json
${{ env.PKG_PATH }}/CHANGELOG.md
# if we have generated changes
- name: Update Github Release notes
if: inputs.previous_version
uses: softprops/action-gh-release@v1
with:
draft: true
tag_name: ${{ env.TAG_PREFIX }}${{ inputs.version }}
body_path: "${{ env.PKG_PATH }}/CHANGES-${{ inputs.version }}.md"
# no changes, use the default changelog for the body
- name: Update Github Release notes
if: ${{!inputs.previous_version}}
uses: softprops/action-gh-release@v1
with:
draft: true
tag_name: ${{ env.TAG_PREFIX }}${{ inputs.version }}
body_path: "${{ env.PKG_PATH }}/CHANGELOG.md"
# let's create a PR for all this, too
- name: Create Pull Request
uses: peter-evans/create-pull-request@v4
with:
title: "Preparing Release ${{ env.TAG_PREFIX }}${{ inputs.version }}"
body: |
Automatic Pull-Request to merge release ${{ env.TAG_PREFIX }}${{ inputs.version }}
trigger-release:
# and trigger the tagging release workflow
uses: ./.github/workflows/release-crypto-nodejs.yml
needs: ['prepare-release']
name: "Trigger release Workflow"
with:
tag: ${{needs.prepare-release.outputs.tag}}
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+138
View File
@@ -0,0 +1,138 @@
name: Release Crypto-Node.js
#
# This workflow releases the crypto-bindings for nodejs
#
# It is triggered when seeing a tag prefixed matching `matrix-sdk-crypto-nodejs-v[0-9]+.*`,
# which then build the native bindings for linux, mac and windows via the CI and uploads
# them to the corresponding Github Release tag. Once they are finished, this workflow will
# package the npm tar.gz and uploads that to the Github Release tag as well, before publishing
# it to npmjs.com automatically.
#
# The usual way to trigger this is by manually triggering the `prep-crypto-nodejs-release`
# workflow. See its documentation for instructions how to use it.
env:
PKG_PATH: "bindings/matrix-sdk-crypto-nodejs"
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: 'aarch64-linux-gnu-gcc'
CARGO_TARGET_I686_UNKNOWN_LINUX_GNU_LINKER: 'i686-linux-gnu-gcc'
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER: 'arm-linux-gnueabihf-gcc'
on:
push:
tags:
- matrix-sdk-crypto-nodejs-v[0-9]+.*
workflow_call:
inputs:
tag:
description: "The tag to build with"
required: true
type: string
secrets:
NPM_TOKEN:
required: true
jobs:
upload-assets:
name: "Upload prebuilt libraries"
strategy:
fail-fast: false
matrix:
include:
# ----------------------------------- Linux
# Use Ubuntu LTS-1 for broader glibc compatibility.
- target: x86_64-unknown-linux-gnu
os: ubuntu-20.04
- target: i686-unknown-linux-gnu
apt_install: gcc-i686-linux-gnu g++-i686-linux-gnu
os: ubuntu-20.04
- target: aarch64-unknown-linux-gnu
os: ubuntu-20.04
apt_install: gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
- target: arm-unknown-linux-gnueabihf
os: ubuntu-20.04
apt_install: gcc-arm-linux-gnueabihf
- target: x86_64-unknown-linux-musl
os: ubuntu-20.04
apt_install: musl-tools
# ----------------------------------- macOS
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-apple-darwin
os: macos-latest
# ----------------------------------- Windows
- target: x86_64-pc-windows-msvc
os: windows-latest
- target: i686-pc-windows-msvc
os: windows-latest
- target: aarch64-pc-windows-msvc
os: windows-latest
runs-on: ${{ matrix.os }}
steps:
# use the given tag
- uses: actions/checkout@v3
name: "Checking out ${{ inputs.tag }}"
if: "${{ inputs.tag }}"
with:
ref: ${{ inputs.tag }}
# use the default
- uses: actions/checkout@v3
if: "${{ !inputs.tag }}"
- name: Install Rust
uses: dtolnay/rust-toolchain@nightly
with:
targets: ${{ matrix.target }}
- name: Install Node.js
uses: actions/setup-node@v3
- name: Load cache
uses: Swatinem/rust-cache@v2
- if: ${{ matrix.apt_install }}
run: |
sudo apt-get update
sudo apt-get install -y ${{ matrix.apt_install }}
- name: Build lib
working-directory: ${{env.PKG_PATH}}
run: |
npm install --ignore-scripts
npx napi build --platform --release --strip --target ${{ matrix.target }}
- name: Upload artifacts to release
uses: softprops/action-gh-release@v1
with:
draft: true
files: ${{env.PKG_PATH}}/*.node
publish-nodejs-package:
name: "Package nodejs package"
runs-on: ubuntu-latest
needs:
- upload-assets
steps:
# use the given tag
- uses: actions/checkout@v3
name: "Checking out ${{ inputs.tag }}"
if: "${{ inputs.tag }}"
with:
ref: ${{ inputs.tag }}
# use the default
- uses: actions/checkout@v3
if: "${{ !inputs.tag }}"
- name: Install Rust
uses: dtolnay/rust-toolchain@nightly
- name: Install Node.js
uses: actions/setup-node@v3
- name: Build lib
working-directory: ${{env.PKG_PATH}}
run: |
npm install --ignore-scripts
npm run build
npm pack
- name: Upload npm package to release
uses: softprops/action-gh-release@v1
with:
draft: true
files: ${{env.PKG_PATH}}/*tgz
- name: Publish to npmjs.com
uses: JS-DevTools/npm-publish@v1
with:
package: ${{env.PKG_PATH}}/package.json
access: public
token: ${{ secrets.NPM_TOKEN }}
+59
View File
@@ -0,0 +1,59 @@
# This workflow releases the `matrix-sdk-crypto-js` project.
#
# It is triggered when a new tag appears that matches
# `matrix-sdk-crypto-js-[0-9]+.*`. This workflow builds the package
# for the bindings, runs its tests to ensure everything is still
# correct, and publishes the package on NPM and on a newly created
# Github release.
name: Release `crypto-js`
env:
CARGO_TERM_COLOR: always
PKG_PATH: "bindings/matrix-sdk-crypto-js"
on:
push:
tags:
- matrix-sdk-crypto-js-[0-9]+.*
jobs:
publish-matrix-sdk-crypto-js:
name: Publish 🕸 [m]-crypto-js
runs-on: ubuntu-latest
steps:
- name: Checkout the repo
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- name: Load cache
uses: Swatinem/rust-cache@v2
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 20.0
- name: Install NPM dependencies
working-directory: ${{ env.PKG_PATH }}
run: npm install
- name: Publish the WebAssembly + JavaScript binding (imply building + testing)
uses: JS-DevTools/npm-publish@v1
with:
package: ${{env.PKG_PATH}}/package.json
access: public
token: ${{ secrets.NPM_TOKEN }}
- name: Create the Github release
uses: softprops/action-gh-release@v1
with:
draft: true
files: ${{ env.PKG_PATH }}/pkg/matrix-org-matrix-sdk-crypto-js-*.tgz
-105
View File
@@ -1,105 +0,0 @@
name: Style
on:
workflow_dispatch:
push:
branches: [main]
pull_request:
branches: [main]
types:
- opened
- reopened
- synchronize
- ready_for_review
env:
CARGO_TERM_COLOR: always
jobs:
style:
name: Check Formatting
runs-on: ubuntu-latest
if: github.event_name == 'push' || !github.event.pull_request.draft
steps:
- name: Checkout the repo
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: rustfmt
profile: minimal
override: true
- name: Cargo fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: -- --check
pre-commit-styles:
name: Check Style
if: github.event_name == 'push' || !github.event.pull_request.draft
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: clippy
profile: minimal
override: true
- name: Setup Python Env
uses: actions/setup-python@v2
- name: Check Styles with pre-commit
uses: pre-commit/action@v2.0.3
with:
# only run `commit`-hooks, excludes typos, clippy, etc
extra_args: --show-diff-on-failure --hook-stage commit
typos:
name: Spell Check with Typos
needs: [style]
runs-on: ubuntu-latest
if: github.event_name == 'push' || !github.event.pull_request.draft
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v2
- name: Check the spelling of the files in our repo
uses: crate-ci/typos@master
clippy:
name: Run clippy
needs: [style]
runs-on: ubuntu-latest
if: github.event_name == 'push' || !github.event.pull_request.draft
steps:
- name: Checkout the repo
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: clippy
profile: minimal
override: true
- name: Load cache
uses: Swatinem/rust-cache@v1
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: run
args: -p xtask -- ci clippy
-75
View File
@@ -1,75 +0,0 @@
name: WASM
on:
push:
branches: [main]
pull_request:
branches: [main]
types:
- opened
- reopened
- synchronize
- ready_for_review
env:
CARGO_TERM_COLOR: always
jobs:
check-wasm:
name: Build test / ${{ matrix.name }}
runs-on: ubuntu-latest
if: github.event_name == 'push' || !github.event.pull_request.draft
strategy:
fail-fast: true
matrix:
name:
- matrix-sdk-qrcode
- matrix-sdk-base
- matrix-sdk-common
- matrix-sdk-crypto
- indexeddb-no-crypto
- indexeddb-with-crypto
include:
- name: matrix-sdk (no-default, wasm-flags)
cmd: matrix-sdk-no-default
- name: matrix-sdk / indexeddb_stores
cmd: matrix-sdk-indexeddb-stores
- name: matrix-sdk / indexeddb_stores / no crypto
cmd: matrix-sdk-indexeddb-stores-no-crypto
- name: matrix-sdk / wasm-example
cmd: matrix-sdk-command-bot
steps:
- name: Checkout the repo
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: wasm32-unknown-unknown
components: clippy
profile: minimal
override: true
- name: Install WasmPack
uses: jetli/wasm-pack-action@v0.3.0
with:
version: 'latest'
- name: Load cache
uses: Swatinem/rust-cache@v1
- name: Rust Check
uses: actions-rs/cargo@v1
with:
command: run
args: -p xtask -- ci wasm ${{ matrix.cmd || matrix.name }}
- name: Wasm-Pack test
uses: actions-rs/cargo@v1
with:
command: run
args: -p xtask -- ci wasm-pack ${{ matrix.cmd || matrix.name }}
+76
View File
@@ -0,0 +1,76 @@
# A reusable github actions workflow that will build xtask, if it is not
# already cached.
#
# It will create a pair of GHA cache entries, if they do not already exist.
# The cache keys take the form `xtask-{os}-{hash}`, where "{os}" is "linux"
# or "macos", and "{hash}" is the hash of the xtask# directory.
#
# The cache keys are written to output variables named "cachekey-{os}".
#
name: Build xtask if necessary
on:
workflow_call:
outputs:
cachekey-linux:
description: "The cache key for the linux build artifact"
value: "${{ jobs.xtask.outputs.cachekey-linux }}"
cachekey-macos:
description: "The cache key for the macos build artifact"
value: "${{ jobs.xtask.outputs.cachekey-macos }}"
env:
CARGO_TERM_COLOR: always
jobs:
xtask:
name: "xtask-${{ matrix.os-name }}"
strategy:
fail-fast: true
matrix:
include:
- os: ubuntu-latest
os-name: 🐧
cachekey-id: linux
- os: macos-12
os-name: 🍏
cachekey-id: macos
runs-on: "${{ matrix.os }}"
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Calculate cache key
id: cachekey
# set a step output variable "cachekey-{os}" that can be referenced in
# the job outputs below.
run: |
echo "cachekey-${{ matrix.cachekey-id }}=xtask-${{ matrix.cachekey-id }}-${{ hashFiles('Cargo.toml', 'xtask/**') }}" >> $GITHUB_OUTPUT
- name: Check xtask cache
uses: actions/cache@v3
id: xtask-cache
with:
path: target/debug/xtask
# use the cache key calculated in the step above. Bit of an awkard
# syntax
key: |
${{ steps.cachekey.outputs[format('cachekey-{0}', matrix.cachekey-id)] }}
- name: Install rust stable toolchain
if: steps.xtask-cache.outputs.cache-hit != 'true'
uses: dtolnay/rust-toolchain@stable
- name: Build
if: steps.xtask-cache.outputs.cache-hit != 'true'
run: |
cargo build -p xtask
outputs:
"cachekey-linux": "${{ steps.cachekey.outputs.cachekey-linux }}"
"cachekey-macos": "${{ steps.cachekey.outputs.cachekey-macos }}"
+16 -1
View File
@@ -1,6 +1,21 @@
Cargo.lock
target
generated
master.zip
emsdk-*
.idea/
.env
.build
.swiftpm
/Package.swift
## User settings
xcuserdata/
.vscode/
## OS garbage
.DS_Store
## Kotlin bindings generated files
bindings/kotlin/.gradle/
bindings/kotlin/buildSrc/build/
bindings/kotlin/buildSrc/.gradle/
-41
View File
@@ -1,41 +0,0 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0
hooks:
- id: check-yaml
- id: check-toml
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-merge-conflict
- id: mixed-line-ending
- repo: local
hooks:
- id: fmt
name: fmt
language: system
types: [file, rust]
entry: cargo fmt -- --check
- id: clippy
name: clippy
stages: [push]
language: system
types: [file, rust]
entry: cargo clippy --all-targets --all
pass_filenames: false
- id: test
name: test
stages: [push]
language: system
files: '\.rs$'
entry: cargo test --lib
pass_filenames: false
- id: typos
name: typos
stages: [push]
language: system
entry: typos
pass_filenames: false
+4 -1
View File
@@ -1,7 +1,10 @@
edition = "2018"
max_width = 100
comment_width = 80
wrap_comments = true
imports_granularity = "Crate"
use_small_heuristics = "Max"
group_imports = "StdExternalCrate"
format_code_in_doc_comments = true
doc_comment_code_block_width = 80
# Workaround for https://github.com/rust-lang/rust.vim/issues/464
edition = "2021"
+8
View File
@@ -4,6 +4,14 @@
Fo = "Fo"
BA = "BA"
UE = "UE"
Ure = "Ure"
OFO = "OFO"
Ot = "Ot"
ket = "ket"
# This is the thead html tag, remove this once typos is updated in the github
# action. 1.3.1 seems to work correctly, while 1.11.0 on the CI seems to get
# this wrong
thead = "thead"
[files]
# Our json files contain a bunch of base64 encoded ed25519 keys which aren't
+102
View File
@@ -0,0 +1,102 @@
# Contributing to matrix-rust-sdk
## Chat rooms
In addition to our [main Matrix room], we have a development room at
[#matrix-rust-sdk-dev:flipdot.org]. Please use it to discuss potential changes,
the overall direction of development and related topics.
[main Matrix room]: https://matrix.to/#/#matrix-rust-sdk:matrix.org
[#matrix-rust-sdk-dev:flipdot.org]: https://matrix.to/#/#matrix-rust-sdk-dev:flipdot.org
## Testing
You can run most of the tests that also happen in CI also using
`cargo xtask ci`. This needs a few dependencies to be installed, as it also runs
automatic WASM tests:
```bash
rustup component add clippy
cargo install cargo-nextest typos-cli wasm-pack
```
If you want to execute only one part of CI, there are a few sub-commands (see
`cargo xtask ci --help`).
Some tests are not automatically run in `cargo xtask ci`, for example the
integration tests that need a running synapse instance. These tests reside in
`./testing/matrix-sdk-integration-testing`. See its
[README](./testing/matrix-sdk-integration-testing/README.md) to easily set up a
synapse for testing purposes.
## Sign off
In order to have a concrete record that your contribution is intentional
and you agree to license it under the same terms as the project's license, we've
adopted the same lightweight approach that the Linux Kernel
(https://www.kernel.org/doc/Documentation/SubmittingPatches), Docker
(https://github.com/docker/docker/blob/master/CONTRIBUTING.md), and many other
projects use: the DCO (Developer Certificate of Origin:
http://developercertificate.org/). This is a simple declaration that you wrote
the contribution or otherwise have the right to contribute it to Matrix:
```
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```
If you agree to this for your contribution, then all that's needed is to
include the line in your commit or pull request comment:
```
Signed-off-by: Your Name <your@email.example.org>
```
We accept contributions under a legally identifiable name, such as your name on
government documentation or common-law names (names claimed by legitimate usage
or repute). Unfortunately, we cannot accept anonymous contributions at this
time.
Git allows you to add this signoff automatically when using the `-s` flag to
`git commit`, which uses the name and email set in your `user.name` and
`user.email` git configs.
If you forgot to sign off your commits before making your pull request and are
on Git 2.17+ you can mass signoff using rebase:
```
git rebase --signoff origin/main
```
Generated
+6253
View File
File diff suppressed because it is too large Load Diff
+77 -2
View File
@@ -1,4 +1,79 @@
[workspace]
members = ["benchmarks", "crates/*", "labs/*", "xtask"]
# xtask and labs should only be compiled when invoked explicitly
members = [
"benchmarks",
"bindings/matrix-sdk-crypto-ffi",
"bindings/matrix-sdk-crypto-js",
"bindings/matrix-sdk-crypto-nodejs",
"bindings/matrix-sdk-ffi",
"crates/*",
"testing/*",
"examples/*",
"labs/*",
"uniffi-bindgen",
"xtask",
]
# xtask, labs, testing and the bindings should only be built when invoked explicitly.
default-members = ["benchmarks", "crates/*"]
resolver = "2"
[workspace.package]
rust-version = "1.65"
[workspace.dependencies]
anyhow = "1.0.68"
assert_matches = "1.5.0"
async-stream = "0.3.3"
async-trait = "0.1.60"
base64 = "0.21.0"
byteorder = "1.4.3"
ctor = "0.2.0"
dashmap = "5.2.0"
eyeball = "0.6.0"
eyeball-im = "0.2.0"
futures-util = { version = "0.3.26", default-features = false, features = ["alloc"] }
http = "0.2.6"
ruma = { git = "https://github.com/ruma/ruma", rev = "0143bd9b9f5dcfcaa835afb76f342c12f014f945", features = ["client-api-c", "compat-user-id"] }
ruma-common = { git = "https://github.com/ruma/ruma", rev = "0143bd9b9f5dcfcaa835afb76f342c12f014f945" }
once_cell = "1.16.0"
serde = "1.0.151"
serde_html_form = "0.2.0"
serde_json = "1.0.91"
thiserror = "1.0.38"
tokio = { version = "1.24", default-features = false, features = ["sync"] }
tracing = { version = "0.1.36", default-features = false, features = ["std"] }
tracing-core = "0.1.30"
uniffi = { git = "https://github.com/mozilla/uniffi-rs", rev = "aa91307b6ac27aae6d5c7ad971b762df952d2745" }
uniffi_bindgen = { git = "https://github.com/mozilla/uniffi-rs", rev = "aa91307b6ac27aae6d5c7ad971b762df952d2745" }
vodozemac = { git = "https://github.com/matrix-org/vodozemac", rev = "fb609ca1e4df5a7a818490ae86ac694119e41e71" }
zeroize = "1.3.0"
# Default release profile, select with `--release`
[profile.release]
lto = true
# Default development profile; default for most Cargo commands, otherwise
# selected with `--debug`
[profile.dev]
# Saves a lot of disk space. If symbols are needed, use the dbg profile.
debug = 0
[profile.dev.package]
# Optimize quote even in debug mode. Speeds up proc-macros enough to account
# for the extra time of optimizing it for a clean build of matrix-sdk-ffi.
quote = { opt-level = 2 }
sha2 = { opt-level = 2 }
# Custom profile with full debugging info, use `--profile dbg` to select
[profile.dbg]
inherits = "dev"
debug = 2
# Custom profile for use in (debug) builds of the binding crates, use
# `--profile reldbg` to select
[profile.reldbg]
inherits = "dev"
incremental = false
# Compile all non-workspace crate in the dependency tree with optimizations
[profile.reldbg.package."*"]
opt-level = 3
+9 -3
View File
@@ -1,8 +1,8 @@
![Build Status](https://img.shields.io/github/workflow/status/matrix-org/matrix-rust-sdk/CI?style=flat-square)
![Build Status](https://img.shields.io/github/actions/workflow/status/matrix-org/matrix-rust-sdk/ci.yml?style=flat-square)
[![codecov](https://img.shields.io/codecov/c/github/matrix-org/matrix-rust-sdk/main.svg?style=flat-square)](https://codecov.io/gh/matrix-org/matrix-rust-sdk)
[![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0)
[![#matrix-rust-sdk](https://img.shields.io/badge/matrix-%23matrix--rust--sdk-blue?style=flat-square)](https://matrix.to/#/#matrix-rust-sdk:matrix.org)
[![Docs - Main](https://img.shields.io/badge/docs-main-blue.svg?style=flat-square)](https://matrix-org.github.io/matrix-rust-sdk/)
[![Docs - Main](https://img.shields.io/badge/docs-main-blue.svg?style=flat-square)](https://matrix-org.github.io/matrix-rust-sdk/matrix_sdk/)
[![Docs - Stable](https://img.shields.io/crates/v/matrix-sdk?color=blue&label=docs&style=flat-square)](https://docs.rs/matrix-sdk)
# matrix-rust-sdk
@@ -26,7 +26,7 @@ The rust-sdk consists of multiple crates that can be picked at your convenience:
## Minimum Supported Rust Version (MSRV)
These crates are built with the Rust language version 2021 and require a minimum compiler version of `1.60`
These crates are built with the Rust language version 2021 and require a minimum compiler version of `1.65`.
## Status
@@ -36,6 +36,12 @@ the API will change in breaking ways.
If you are interested in using the matrix-sdk now is the time to try it out and
provide feedback.
## Bindings
Some crates of the **matrix-rust-sdk** can be embedded inside other
environments, like Swift, Kotlin, JavaScript, Node.js etc. Please,
explore the [`bindings/`](./bindings/) directory to learn more.
## License
[Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0)
-43
View File
@@ -1,43 +0,0 @@
# SDK 0.5 - stores and native crypto
The @matrix-org/rust team is happy to present to the wider public version 0.5 of the [`matrix-rust-sdk`][sdk], now available on crates.io for your convenience. This marks an important milestone after over half a year of work since the latest release (0.4) and with many new team members working on it. It comes with many bug fixes, improvements and updates, but a few we'd like to highlight here:
## Better, safer, native-er crypto
One of the biggest improvements under the hood is the replacement of the former crypto core written in C, libolm, with the fully rustic, fully audited [Vodozemac][vodozemac]. [Vodozemac][vodozemac] is a much more robust new implementation of the olm crypto primitives and engines with all the learning included and pitfalls from the previous C implementation avoided in a freshly baked, very robust library. With this release both matrix-sdk-crypto and matrix-sdk (when the `e2e-encryption` is enabled, which it is by default) use vodozemac to handle all crypto needs.
## Stores, stores, stores
This release has also seen a major refactoring around the state and crypto store primitives and implementations. From this release forward, the implementation of the storage layer is truly modular and pluggable, with the default implementation (based on `sled`) even being a separate crate. Furthermore this release has also additional support for the [`indexeddb`-storage][indexeddb] layer for in-browser WASM needs. As before, the base still ships with an in-memory store, but if none of them fits your needs, you are very much invited to build your own - we recommend looking at the implementation of the existing [`matrix-sdk-sled`][sled] to understand what is needed.
We've further extended and cleaned up the storage API and hardened the two existing implementations: both `sled` and `indexeddb` will now encrypt _all metadata_, including all keys, if a passphrase is given. Even a core dump of a database won't show any strings or other identifiable data. Both implementation also hold a database version string now, allowing future migrations of the database schema while we move forward.
## WebAssembly
It already came through in the previous paragraph: we now fully support `wasm` as a primary target for the main sdk and its dependencies (browser for now). While it was already possible to build the SDK for wasm before, if you were lucky and had the specific emscripten setup, even crypto didn't always fail to compile, our move to vodozemac frees us from these problems and our CI now makes sure all our PRs will continue to build on WebAssembly, too. And with the `indexeddb`-store, we also have a fully persistent storage layer implementation for all your in-browser matrix needs built in.
## More features
Of course a lot more has happened over the last few months as we've ramped up our work on the SDK. Most notably, you can now check whether a room has been created as a `space`, and we offer nice automatic thumbnailing and resizing support if you enable `image`. Additionally, there is a very early event-timeline API behind the `experimental-timeline`-feature-flag. Just to name a few. We really recommend you migrate from the older version to this one.
## Process and Workflow
We've also ramped up our CI, parallelized it a lot, while adding more tests. In general we don't merge anything breaking the tests, thus our `main` can also be considered `stable` to build again, though things might be changed without prior notice. We recommend keeping an eye out in our [team chat][chat] to stay current.
### Versions
This and all future releases of matrix-sdk adhere to [semver][semver] and we do our best to stick to it for the default-feature-set. As you will see only the main crate `matrix-sdk` is actually versioned at `0.5`, while other crates - especially newer ones - have different versions attached. We recommend to sticking to the high-level matrix-sdk-crate wherever reasonable as lower level crates might change more often, also in breaking fashion.
With this release all our crates are build with `rust v2021` and need a rust compiler of at least `1.60` (due to our need for weak-dependencies).
### Conventional Commit
We also want to make it easier for external parties to follow changes and stay up to date with what's-what, so, with this release, we are implementing the [conventional commits][cc] standard for our commits and will start putting in automatic changelog generation. Which makes it easier for us to create comprehensive changelogs for releases, but also for anyone following `main` to figure out what changed.
[sdk]: https://crates.io/crates/matrix-sdk/
[vodozemac]: https://github.com/matrix-org/vodozemac
[sled]: https://crates.io/crates/matrix-sdk-sled/
[indexeddb]: https://crates.io/crates/matrix-sdk-indexeddb/
[chat]: https://matrix.to/#/#matrix-rust-sdk:matrix.org
[semver]: https://semver.org/
[cc]: http://conventionalcommits.org/
+121
View File
@@ -0,0 +1,121 @@
# Upgrades 0.5 ➜ 0.6
This is a rough migration guide to help you upgrade your code using matrix-sdk 0.5 to the newly released matrix-sdk 0.6 . While it won't cover all edge cases and problems, we are trying to get the most common issues covered. If you experience any other difficulties in upgrade or need support with using the matrix-sdk in general, please approach us in our [matrix-sdk channel on matrix.org][matrix-channel].
## Minimum Supported Rust Version Update: `1.60`
We have updated the minimal rust version you need in order to build `matrix-sdk`, as we require some new dependency resolving features from it:
> These crates are built with the Rust language version 2021 and require a minimum compiler version of 1.60
## Dependencies
Many dependencies have been upgraded. Most notably, we are using `ruma` at version `0.7.0` now. It has seen some renamings and restructurings since our last release, so you might find that some Types have new names now.
## Repo Structure Updates
If you are looking at the repository itself, you will find we've rearranged the code quite a bit: we have split out any bindings-specific and testing related crates (and other things) into respective folders, and we've moved all `examples` into its own top-level-folder with each example as their own crate (rendering them easier to find and copy as starting points), all in all slimming down the `crates` folder to the core aspects.
## Architecture Changes / API overall
### Builder Pattern
We are moving to the [builder pattern][] (familiar from e.g. `std::io:process:Command`) as the main configurable path for many aspects of the API, including to construct Matrix-Requests and workflows. This has been and is an on-going effort, and this release sees a lot of APIs transitioning to this pattern, you should already be familiar with from the `matrix_sdk::Client::builder()` in `0.5`. This pattern been extended onto:
- the [login configuration][login builder] and [login with sso][ssologin builder],
- [`SledStore` configuratiion][sled-store builder]
- [`Indexeddb` configuration][indexeddb builder]
Most have fallback (though maybe with deprecation warning) support for an existing code path, but these are likely to be removed in upcoming releases.
### Splitting of concerns: Media
In an effort to declutter the `Client` API dedicated types have been created dealing with specific concerns in one place. In `0.5` we introduced `client.account()`, and `client.encryption()`, we are doing the same with `client.media()` to manage media and attachments in one place with the [`media::Media` type][media typ] now.
The signatures of media uploads, have also changed slightly: rather than expecting a reader `R: Read + Seek`, it now is a simple `&[u8]`. Which also means no more unnecessary `seek(0)` to reset the cursor, as we are just taking an immutable reference now.
### Event Handling & sync updaes
If you are using the `client.register_event_handler` function to receive updates on incoming sync events, you'll find yourself with a deprecation warning now. That is because we've refactored and redesigned the event handler logic to allowing `removing` of event handlers on the fly, too. For that the new `add_event_handler()` (and `add_room_event_handler`) will hand you an `EventHandlerHandle` (pardon the pun), which you can pass to `remove_event_handler`, or by using the convenient `client.event_handler_drop_guard` to create a `DropGuard` that will remove the handler when the guard is dropped. While the code still works, we recommend you switch to the new one, as we will be removing the `register_event_handler` and `register_event_handler_context` in a coming release.
Secondly, you will find a new [`sync_with_result_callback` sync function][sync with result]. Other than the previous sync functions, this will pass the entire `Result` to your callback, allowing you to handle errors or even raise some yourself to stop the loop. Further more, it will propagate any unhandled errors (it still handles retries as before) to the outer caller, allowing the higher level to decide how to handle that (e.g. in case of a network failure). This result-returning-behavior also punshes through the existing `sync` and `sync_with_callback`-API, allowing you to handle them on a higher level now (rather than the futures just resolving). If you find that warning, just adding a `?` to the `.await` of the call is probably the quickest way to move forward.
### Refresh Tokens
This release now [supports `refresh_token`s][refresh tokens PR] as part of the [`Session`][session]. It is implemented with a default-flag in serde so deserializing a previously serialized Session (e.g. in a store) will work as before. As part of `refresh_token` support, you can now configure the client via `ClientBuilder.request_refresh_token()` to refresh the access token automagically on certain failures or do it manually by calling `client.refresh_access_token()` yourself. Auto-refresh is _off_ by default.
You can stay informed about updates on the access token by listening to `client.session_tokens_signal()`.
### Further changes
- [`MessageOptions`][message options] has been updated to Matrix 1.3 by making the `from` parameter optional (and function signatures have been updated, too). You can now request the server sends you messages from the first one you are allowed to have received.
- `client.user_id()` is not a `future` anymore. Remove any `.await` you had behind it.
- `verified()`, `blacklisted()` and `deleted()` on `matrix_sdk::encryption::identities::Device` have been renamed with a `is_` prefix.
- `verified()` on `matrix_sdk::encryption::identities::UserIdentity`, too has been prefixed with `is_` and thus is onw called `is_verified()`.
- The top-level crypto and state-store types of Indexeddb and Sled have been renamed to unique types>
- `state_store` and `crypto_store` do not need to be boxed anymore when passed to the [`StoreConfig`][store config]
- Indexeddb's `SerializationError` is now `IndexedDBStoreError`
- Javascript specific features are now behind the `js` feature-gate
- The new experimental next generation of sync ("sliding sync"), with a totally revamped api, can be found behind the optional `sliding-sync`-feature-gate
## Quick Troubleshooting
You find yourself focussed with any of these, here are the steps to follow to upgrade your code accordingly:
### warning: use of deprecated associated function `matrix_sdk::Client::register_event_handler`: Use [`Client::add_event_handler`](#method.add_event_handler) instead
As it says on the tin: we have deprecated this function in favor of the newer removable handler approach (see above). You can still continue to use this `fn` for now, but it will be removed in a future release.
### warning: use of deprecated associated function `matrix_sdk::Client::login`: Replaced by [`Client::login_username`](#method.login_username)
We have replaced the login facilities with a `LoginBuilder` and recommend you use that from now on. This isn't an error yet, but the function will be removed in a future release.
### expected slice `[u8]`, found struct ...
We've updated the `send_attachment` and `Media` signatures to use `&[u8]` rather than `reader: Read + Seek` as it is more convenient and common place for most architectures anyways. If you are using `File::open(path)?` to get that handler, you can just replace that with `std::fs::read(path)?`
### no method named `verified` found for struct `matrix_sdk::encryption::identities::Device` in the current scope
Boolean flags like `verified`, `deleted`, `blacklisted`, etc have been renamed with a `is_` prefix. So, just follow the cargo suggestion:
```
|
69 | device.verified()
| ^^^^^^^^ help: there is an associated function with a similar name: `is_verified`
```
### unresolved import `matrix_sdk::ruma::events::AnySyncRoomEvent`
Ruma has been updated to `0.7.0`, you will find some ruma Events names have changed, most notably, the `AnySyncRoomEvent` is now named `AnySyncTimelineEvent` (and not `AnySyncStateEvent`, which cargo wrongly suggests). Just rename the import and usage of it.
### `std::option::Option<&matrix_sdk::ruma::UserId>` is not a future
You are seeing something along the lines of:
```
19 | if room_member.state_key != client.user_id().await.unwrap() {
| ^^^^^^ `std::option::Option<&matrix_sdk::ruma::UserId>` is not a future
|
= help: the trait `Future` is not implemented for `std::option::Option<&matrix_sdk::ruma::UserId>`
= note: std::option::Option<&matrix_sdk::ruma::UserId> must be a future or must implement `IntoFuture` to be awaited
= note: required because of the requirements on the impl of `IntoFuture` for `std::option::Option<&matrix_sdk::ruma::UserId>`
help: remove the `.await`
|
19 - if room_member.state_key != client.user_id().await.unwrap() {
19 + if room_member.state_key != client.user_id().unwrap() {
```
You are using `client.user_id().await` but `user_id()` is no longer `async`. Just follow the cargo suggestion and remove the `.await`, it is not necessary any longer.
[matrix-channel]: https://matrix.to/#/#matrix-rust-sdk:matrix.org
[builder pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
[login builder]: https://docs.rs/matrix-sdk/latest/matrix_sdk/struct.LoginBuilder.html
[ssologin builder]: https://docs.rs/matrix-sdk/latest/matrix_sdk/struct.SsoLoginBuilder.html
[sled-store builder]: https://docs.rs/matrix-sdk-sled/latest/matrix_sdk_sled/struct.SledStateStoreBuilder.html
[indexeddb builder]: https://docs.rs/matrix-sdk-indexeddb/latest/matrix_sdk_indexeddb/struct.IndexeddbStateStoreBuilder.html
[media type]: https://docs.rs/matrix-sdk/latest/matrix_sdk//media/struct.Media.html
[sync with result]: https://docs.rs/matrix-sdk/latest/matrix_sdk/struct.Client.html#method.sync_with_result_callback
[session]: https://docs.rs/matrix-sdk/latest/matrix_sdk/struct.Session.html
[refresh tokens PR]: https://github.com/matrix-org/matrix-rust-sdk/pull/892
[store config]: https://docs.rs/matrix-sdk-base/latest/matrix_sdk_base/store/struct.StoreConfig.html
[message options]: https://docs.rs/matrix-sdk/latest/matrix_sdk/room/struct.MessagesOptions.html
+9 -9
View File
@@ -3,22 +3,22 @@ name = "benchmarks"
description = "Matrix SDK benchmarks"
edition = "2021"
license = "Apache-2.0"
rust-version = "1.56"
rust-version = { workspace = true }
version = "1.0.0"
publish = false
[dependencies]
criterion = { version = "0.3.5", features = ["async", "async_tokio", "html_reports"] }
matrix-sdk-crypto = { path = "../crates/matrix-sdk-crypto", version = "0.5.0" }
matrix-sdk-sled = { path = "../crates/matrix-sdk-sled", version = "0.1.0", default-features = false, features = ["crypto-store"] }
matrix-sdk-test = { path = "../crates/matrix-sdk-test", version = "0.5.0" }
ruma = "0.6.1"
serde_json = "1.0.79"
criterion = { version = "0.4.0", features = ["async", "async_tokio", "html_reports"] }
matrix-sdk-crypto = { path = "../crates/matrix-sdk-crypto", version = "0.6.0"}
matrix-sdk-sled = { path = "../crates/matrix-sdk-sled", version = "0.2.0", default-features = false, features = ["crypto-store"] }
matrix-sdk-test = { path = "../testing/matrix-sdk-test", version = "0.6.0"}
ruma = { workspace = true }
serde_json = { workspace = true }
tempfile = "3.3.0"
tokio = { version = "1.17.0", default-features = false, features = ["rt-multi-thread"] }
tokio = { version = "1.24.2", default-features = false, features = ["rt-multi-thread"] }
[target.'cfg(target_os = "linux")'.dependencies]
pprof = { version = "0.8.0", features = ["flamegraph", "criterion"] }
pprof = { version = "0.11.0", features = ["flamegraph", "criterion"] }
[[bench]]
name = "crypto_bench"
+12 -12
View File
@@ -1,8 +1,8 @@
use std::ops::Deref;
use std::{ops::Deref, sync::Arc};
use criterion::*;
use matrix_sdk_crypto::{EncryptionSettings, OlmMachine};
use matrix_sdk_sled::CryptoStore as SledCryptoStore;
use matrix_sdk_sled::SledCryptoStore;
use matrix_sdk_test::response_from_file;
use ruma::{
api::{
@@ -63,7 +63,7 @@ pub fn keys_query(c: &mut Criterion) {
let mut group = c.benchmark_group("Keys querying");
group.throughput(Throughput::Elements(count as u64));
let name = format!("{} device and cross signing keys", count);
let name = format!("{count} device and cross signing keys");
group.bench_with_input(BenchmarkId::new("memory store", &name), &response, |b, response| {
b.to_async(&runtime)
@@ -71,7 +71,7 @@ pub fn keys_query(c: &mut Criterion) {
});
let dir = tempfile::tempdir().unwrap();
let store = Box::new(SledCryptoStore::open_with_passphrase(dir.path(), None).unwrap());
let store = Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
let machine =
runtime.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store)).unwrap();
@@ -96,7 +96,7 @@ pub fn keys_claiming(c: &mut Criterion) {
let mut group = c.benchmark_group("Olm session creation");
group.throughput(Throughput::Elements(count as u64));
let name = format!("{} one-time keys", count);
let name = format!("{count} one-time keys");
group.bench_with_input(BenchmarkId::new("memory store", &name), &response, |b, response| {
b.iter_batched(
@@ -119,7 +119,7 @@ pub fn keys_claiming(c: &mut Criterion) {
|| {
let dir = tempfile::tempdir().unwrap();
let store =
Box::new(SledCryptoStore::open_with_passphrase(dir.path(), None).unwrap());
Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
let machine = runtime
.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store))
@@ -158,12 +158,12 @@ pub fn room_key_sharing(c: &mut Criterion) {
let mut group = c.benchmark_group("Room key sharing");
group.throughput(Throughput::Elements(count as u64));
let name = format!("{} devices", count);
let name = format!("{count} devices");
group.bench_function(BenchmarkId::new("memory store", &name), |b| {
b.to_async(&runtime).iter(|| async {
let requests = machine
.share_group_session(
.share_room_key(
room_id,
users.iter().map(Deref::deref),
EncryptionSettings::default(),
@@ -181,7 +181,7 @@ pub fn room_key_sharing(c: &mut Criterion) {
})
});
let dir = tempfile::tempdir().unwrap();
let store = Box::new(SledCryptoStore::open_with_passphrase(dir.path(), None).unwrap());
let store = Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
let machine =
runtime.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store)).unwrap();
@@ -191,7 +191,7 @@ pub fn room_key_sharing(c: &mut Criterion) {
group.bench_function(BenchmarkId::new("sled store", &name), |b| {
b.to_async(&runtime).iter(|| async {
let requests = machine
.share_group_session(
.share_room_key(
room_id,
users.iter().map(Deref::deref),
EncryptionSettings::default(),
@@ -225,7 +225,7 @@ pub fn devices_missing_sessions_collecting(c: &mut Criterion) {
let mut group = c.benchmark_group("Devices missing sessions collecting");
group.throughput(Throughput::Elements(count as u64));
let name = format!("{} devices", count);
let name = format!("{count} devices");
runtime.block_on(machine.mark_request_as_sent(&txn_id, &response)).unwrap();
@@ -236,7 +236,7 @@ pub fn devices_missing_sessions_collecting(c: &mut Criterion) {
});
let dir = tempfile::tempdir().unwrap();
let store = Box::new(SledCryptoStore::open_with_passphrase(dir.path(), None).unwrap());
let store = Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
let machine =
runtime.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store)).unwrap();
+41
View File
@@ -0,0 +1,41 @@
## Introduction
**matrix-rust-sdk** leverages [UniFFI](https://mozilla.github.io/uniffi-rs/) to generate bindings for host languages (eg. Swift and Kotlin).
Rust code related with bindings live in the [matrix-rust-sdk/bindings](https://github.com/matrix-org/matrix-rust-sdk/tree/main/bindings) folder.
Developers can expose Rust code to UniFFI using two different approaches:
- Using an `.udl` file. When a crate has one, you find it under the `src` folder (an example is [here](https://github.com/matrix-org/matrix-rust-sdk/blob/main/bindings/matrix-sdk-ffi/src/api.udl)).
- Add UniFFI directivies as Rust attributes. In this case Rust source files (`.rs`) contain attributes related to UniFFI (e.g. `#[uniffi::export]`). Attributes are preferred, where applicable.
## Expose Rust definitions to UniFFI
### Check if the API is already on UniFFI
First of all check if the Rust definition you are looking for exists on UniFFI already. Most of exposed matrix definitions are collected in the crate [matrix-sdk-ffi](https://github.com/matrix-org/matrix-rust-sdk/tree/main/bindings/matrix-sdk-ffi).
This crate contains mainly small Rust wrappers around the actual Rust SDK (e.g. the crate [matrix-sdk](https://github.com/matrix-org/matrix-rust-sdk/tree/main/crates/matrix-sdk))
If the Rust definition is on UniFFI already, you either:
- find it in a `.udl` file like [matrix-sdk-ffi/src/api.udl](https://github.com/matrix-org/matrix-rust-sdk/blob/main/bindings/matrix-sdk-ffi/src/api.udl)
- see it marked with a proper UniFFI Rust attribute like this `#[uniffi::export]`
### Adding a missing matrix API
1. Unless you want to contribute on the crypto side, you probably need to add some code in the [matrix-sdk-ffi](https://github.com/matrix-org/matrix-rust-sdk/tree/main/bindings/matrix-sdk-ffi) crate. After you find the crate you need to understand which file is best to contain the new Rust definition. When exposing new matrix API often (but not always) you need to touch the file [client.rs](https://github.com/matrix-org/matrix-rust-sdk/blob/main/bindings/matrix-sdk-ffi/src/client.rs)
2. Identify the API to expose in the target Rust crate (typically in [matrix-sdk](https://github.com/matrix-org/matrix-rust-sdk/tree/main/crates/matrix-sdk). If you cant find it, you probably need to touch the actual Rust SDK as well. In this case you typically just need to write some code around [ruma](https://github.com/ruma/ruma) (a Rust SDKs dependency) which already implements most of the matrix protocol
3. After you got (by finding or writing) the required Rust code, you need to expose to UniFFI. To do that just write a small Rust wrapper in the related UniFFI crate (most of the time is **matrix-sdk-ffi**) you found on step 1.
4. When your new (wrapping) Rust definition is ready, remember to expose it to UniFFI.
Its best to do it using UniFFI Rust attributes (e.g. `#[uniffi::export]`). Otherwise add the new definition in the crates `.udl` file. For the **matrix-sdk-ffi** crate the definition file is [api.udl](https://github.com/matrix-org/matrix-rust-sdk/blob/main/bindings/matrix-sdk-ffi/src/api.udl). **Remember**: the language inside a `.udl` file isnt Rust. To learn more about how map Rust into UDL read [here](https://mozilla.github.io/uniffi-rs/udl_file_spec.html)
## FAQ
**Q**: I wrote my Rust code and exposed it to UniFFI. How can I check if the compiler is happy?\
**A**: Run `cargo build` in the crate you touched (e.g. matrix-sdk-ffi). The compiler will complain if the Rust code and/or the `.udl` is wrong.
**Q**: The compiler is happy with my code but the CI is failing on GitHub. How can I fix it?\
**A**: The CI may fail for different reasons, you need to have a look on the failing GitHub workflow. One common reason though is that the linter ([Clippy](https://github.com/rust-lang/rust-clippy)) isnt happy with your code. If this is the case, you can run `cargo clippy` in the crate you touched to see whats wrong and fix it accordingly.
+25
View File
@@ -0,0 +1,25 @@
# Matrix Rust SDK bindings
In this directory, one can find bindings to the Rust SDK that are
maintained by the owners of the Matrix Rust SDK project.
* [`apple`] or `matrix-rust-components-swift`, Swift bindings of the
[`matrix-sdk`] crate via [`matrix-sdk-ffi`],
* [`matrix-sdk-crypto-ffi`], UniFFI (Kotlin, Swift, Python, Ruby) bindings of the [`matrix-sdk-crypto`]
crate,
* [`matrix-sdk-crypto-js`], JavaScript bindings of the
[`matrix-sdk-crypto`] crate,
* [`matrix-sdk-crypto-nodejs`], Node.js bindings of the
[`matrix-sdk-crypto`] crate,
* [`matrix-sdk-ffi`], UniFFI bindings of the [`matrix-sdk`] crate.
[`apple`]: ./apple
[`matrix-sdk-crypto-ffi`]: ./matrix-sdk-crypto-ffi
[`matrix-sdk-crypto-js`]: ./matrix-sdk-crypto-js
[`matrix-sdk-crypto-nodejs`]: ./matrix-sdk-crypto-nodejs
[`matrix-sdk-crypto`]: ../crates/matrix-sdk-crypto
[`matrix-sdk-ffi`]: ./matrix-sdk-ffi
[`matrix-sdk`]: ../crates/matrix-sdk
# Contributing
To contribute read this [guide](./CONTRIBUTING.md).
+8
View File
@@ -0,0 +1,8 @@
// swift-tools-version:5.6
// A package manifest for local development. This file will be copied
// into the root of the repo when generating an XCFramework.
import PackageDescription
let package = Package()
+24
View File
@@ -0,0 +1,24 @@
// swift-tools-version:5.6
// A package manifest for local development. This file will be copied
// into the root of the repo when generating an XCFramework.
import PackageDescription
let package = Package(
name: "MatrixRustSDK",
platforms: [
.iOS(.v15),
.macOS(.v12)
],
products: [
.library(name: "MatrixRustSDK",
targets: ["MatrixRustSDK"]),
],
targets: [
.binaryTarget(name: "MatrixSDKFFI", path: "bindings/apple/generated/MatrixSDKFFI.xcframework"),
.target(name: "MatrixRustSDK",
dependencies: [.target(name: "MatrixSDKFFI")],
path: "bindings/apple/generated/swift")
]
)
@@ -0,0 +1,21 @@
# Podspec file for local-development only, which points to local version of generated framework instead of github release
# To use this, the file needs to be copied to the root of `matrix-rust-sdk` in order to have access to the source files.`
Pod::Spec.new do |s|
s.name = "MatrixSDKCrypto"
s.version = "0.0.1"
s.summary = "Uniffi based bindings for the Rust SDK crypto crate."
s.homepage = "https://github.com/matrix-org/matrix-rust-sdk"
s.license = { :type => "Apache License, Version 2.0", :file => "LICENSE" }
s.author = { "matrix.org" => "support@matrix.org" }
s.ios.deployment_target = "11.0"
s.osx.deployment_target = "10.10"
s.swift_versions = ['5.1', '5.2']
s.source = { :git => "Not Published", :tag => "Cocoapods/#{s.name}/#{s.version}" }
s.vendored_frameworks = "generated/MatrixSDKCryptoFFI.xcframework"
s.source_files = "generated/Sources/**/*.{swift}"
s.resources = ["bindings/matrix-sdk-crypto-ffi/src/**/*.rs", "crates/matrix-sdk-crypto/src/**/*.rs"]
end
+19
View File
@@ -0,0 +1,19 @@
Pod::Spec.new do |s|
s.name = "MatrixSDKCrypto"
s.version = "0.0.1" # Version is only incremented manually and locally before pushing to CocoaPods
s.summary = "Uniffi based bindings for the Rust SDK crypto crate."
s.homepage = "https://github.com/matrix-org/matrix-rust-sdk"
s.license = { :type => "Apache License, Version 2.0", :file => "LICENSE" }
s.author = { "matrix.org" => "support@matrix.org" }
s.ios.deployment_target = "11.0"
s.osx.deployment_target = "10.10"
s.swift_versions = ['5.0']
s.source = { :http => "https://github.com/matrix-org/matrix-rust-sdk/releases/download/matrix-sdk-crypto-ffi-#{s.version}/MatrixSDKCryptoFFI.zip" }
s.vendored_frameworks = "MatrixSDKCryptoFFI.xcframework"
s.source_files = "Sources/**/*.{swift}"
end
+33
View File
@@ -0,0 +1,33 @@
// swift-tools-version: 5.6
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MatrixRustSDK",
platforms: [
.iOS(.v15),
.macOS(.v12)
],
products: [
.library(name: "MatrixRustSDK",
targets: ["MatrixRustSDK"]),
],
targets: [
.target(name: "MatrixRustSDK",
path: "generated/swift",
swiftSettings: [
.unsafeFlags(["-I", "./generated/matrix_sdk_ffi"])
]),
.testTarget(name: "MatrixRustSDKTests",
dependencies: ["MatrixRustSDK"],
swiftSettings: [
.unsafeFlags(["-I", "./generated/matrix_sdk_ffi"])
],
linkerSettings: [
.linkedLibrary("matrix_sdk_ffi", .when(platforms: [.macOS])),
.linkedLibrary("matrix_sdk_ffiFFI", .when(platforms: [.linux])),
.unsafeFlags(["-L./generated/matrix_sdk_ffi"])
])
]
)
+73
View File
@@ -0,0 +1,73 @@
# Apple platforms support
This project and build script demonstrate how to create an XCFramework that can be imported into an Xcode project and run on Apple platforms. It can compile and bundle an [entire SDK](#Building-the-SDK), or only a smaller [Crypto module](#Building-only-the-Crypto-SDK) that provides end-to-end encryption for clients that already depend on an SDK (e.g. [Matrix iOS SDK](https://github.com/matrix-org/matrix-ios-sdk))
## Building
### Prerequisites for building universal frameworks
- the Rust toolchain
- Apple targets (e.g. `rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios aarch64-apple-darwin x86_64-apple-darwin`)
- `xcodebuild` command line tool from [Apple](https://developer.apple.com/library/archive/technotes/tn2339/_index.html)
- `lipo` for creating the fat static libs
### Building the SDK
```
cargo xtask swift build-framework
```
The `build-framework` task will go through all the steps required to generate a fully usable `.xcframework`:
1. compile `matrix-sdk-ffi` libraries for iOS, the iOS simulator and macOS under `/target`. Some targets are not part of the standard library and they will be built using the nightly toolchain.
2. `lipo` together the libraries for the same platform under `/generated`
3. run `uniffi` and generate the C header, module map and swift files
4. `xcodebuild` an `xcframework` from the fat static libs and the original iOS one, and add the header and module map to it under `generated/MatrixSDKFFI.xcframework`
5. cleanup and delete the generated files except the .xcframework and the swift sources (that aren't part of the framework)
For development purposes, it will additionally generate a `Package.swift` file in the root of the repo that can be used to add the framework to your project and enable debugging through the use of [rust-xcode-plugin](https://github.com/BrainiumLLC/rust-xcode-plugin) (make sure to run the task with the argument `--profile=reldbg`).
When building the SDK for release you should pass the `--release` argument to the task, which will strip away any symbols and optimise the created binary.
### Building only the Crypto SDK
```
build_crypto_xcframework.sh
```
The `build_crypto_xcframework.sh` script will go through all the steps required to generate a fully usable `.xcframework`:
1. compile `matrix-sdk-crypto-ffi` libraries for iOS and the iOS simulator under `/target`
2. `lipo` together the libraries for the same platform under `/generated`
3. run `uniffi` and generate the C header, module map and swift files
4. `xcodebuild` an `xcframework` from the fat static libs and the original iOS one, and add the header and module map to it under `generated/MatrixSDKCryptoFFI.xcframework`
5. cleanup and delete the generated files except the .xcframework and the swift sources (that aren't part of the framework)
### Building & testing the Swift package
The `Package.swift` file in this directory provides a simple example on how to integrate everything together but also a place to run unit and integration tests from.
It's pre-configured to link to the generated static lib and .swift files so successfully running `cargo xtask swift build-library` first is necessary for it to compile. Afterwards you can execute the tests with `swift test`. Note that for the moment this only works on macOS but we're planning to add Linux support in the future.
## Distribution
The generated framework and Swift code can be distributed and integrated directly but in order to make things simpler we bundle them together as a [Swift package](https://github.com/matrix-org/matrix-rust-components-swift/) in the case of SDK, and as a CocoaPods podspec in the case of Crypto SDK.
### Publishing MatrixSDKCrypto
1. Navigate into `bindings/apple` and run [`build_crypto_xcframework.sh`](#building-only-the-crypto-sdk) script which generates a .zip file with the framework in `./generated` folder
Note that whilst you can run this command from any git branch, if you want to make a public release, ensure you are building from latest `main`
2. Tag the commit you just used to build the release and push the tag to GitHub
Use `matrix-sdk-crypto-ffi-<major>.<minor>.<patch>` tag name
3. Create a new [GitHub release](https://github.com/matrix-org/matrix-rust-sdk/releases) using this tag (see [example](https://github.com/matrix-org/matrix-rust-sdk/releases/tag/matrix-sdk-crypto-ffi-0.3.4))
4. Upload the previously generated .zip file to this release
5. Increment the version in [`MatrixSDKCrypto.podspec`](./MatrixSDKCrypto.podspec)
Note that this is not automated and is a local-only change. To determine the most recent published version, run `pod repo update && pod search MatrixSDKCrypto`
or check git tags via `git tag | grep matrix-sdk-crypto-ffi`
6. Push new Podspec version to Cocoapods via `pod trunk push MatrixSDKCrypto.podspec --allow-warnings`
@@ -0,0 +1,61 @@
import XCTest
@testable import MatrixRustSDK
final class ClientTests: XCTestCase {
func testBuildingWithHomeserverURL() {
do {
_ = try ClientBuilder()
.homeserverUrl(url: "https://localhost:8008")
.build()
} catch {
XCTFail("The client should build successfully when given a homeserver.")
}
}
func testBuildingWithHomeserverURLAndUserAgent() {
do {
_ = try ClientBuilder()
.homeserverUrl(url: "https://localhost:8008")
.userAgent(userAgent: "golden-eye/007")
.build()
} catch {
XCTFail("The client should build successfully when given a homeserver and user agent.")
}
}
func testBuildingWithUsername() {
do {
_ = try ClientBuilder()
.username(username: "@test:matrix.org")
.build()
} catch {
XCTFail("The client should build successfully when given a username.")
}
}
func testBuildingWithInvalidUsername() {
do {
_ = try ClientBuilder()
.username(username: "@test:invalid")
.build()
XCTFail("The client should not build when given an invalid username.")
} catch ClientError.Generic(let message) {
XCTAssertTrue(message.contains(".well-known"), "The client should fail to do the well-known lookup.")
} catch {
XCTFail("Not expecting any other kind of exception")
}
}
// MARK: - Private
static private var basePath: String {
guard let url = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
fatalError("Should always be able to retrieve the caches directory")
}
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: false, attributes: nil)
return url.path
}
}
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/env bash
set -eEu
cd "$(dirname "$0")"
# Path to the repo root
SRC_ROOT=../..
TARGET_DIR="${SRC_ROOT}/target"
GENERATED_DIR="${SRC_ROOT}/generated"
if [ -d "${GENERATED_DIR}" ]; then rm -rf "${GENERATED_DIR}"; fi
mkdir -p ${GENERATED_DIR}/{macos,simulator}
REL_FLAG="--release"
REL_TYPE_DIR="release"
TARGET_CRATE=matrix-sdk-crypto-ffi
# Build static libs for all the different architectures
# Required by olm-sys crate
export IOS_SDK_PATH=`xcrun --show-sdk-path --sdk iphoneos`
# iOS
echo -e "Building for iOS [1/5]"
cargo build -p ${TARGET_CRATE} ${REL_FLAG} --target "aarch64-apple-ios"
# MacOS
echo -e "\nBuilding for macOS (Apple Silicon) [2/5]"
cargo build -p ${TARGET_CRATE} ${REL_FLAG} --target "aarch64-apple-darwin"
echo -e "\nBuilding for macOS (Intel) [3/5]"
cargo build -p ${TARGET_CRATE} ${REL_FLAG} --target "x86_64-apple-darwin"
# iOS Simulator
echo -e "\nBuilding for iOS Simulator (Apple Silicon) [4/5]"
cargo build -p ${TARGET_CRATE} ${REL_FLAG} --target "aarch64-apple-ios-sim"
echo -e "\nBuilding for iOS Simulator (Intel) [5/5]"
cargo build -p ${TARGET_CRATE} ${REL_FLAG} --target "x86_64-apple-ios"
echo -e "\nCreating XCFramework"
# Lipo together the libraries for the same platform
# MacOS
lipo -create \
"${TARGET_DIR}/x86_64-apple-darwin/${REL_TYPE_DIR}/libmatrix_sdk_crypto_ffi.a" \
"${TARGET_DIR}/aarch64-apple-darwin/${REL_TYPE_DIR}/libmatrix_sdk_crypto_ffi.a" \
-output "${GENERATED_DIR}/macos/libmatrix_sdk_crypto_ffi.a"
# iOS Simulator
lipo -create \
"${TARGET_DIR}/x86_64-apple-ios/${REL_TYPE_DIR}/libmatrix_sdk_crypto_ffi.a" \
"${TARGET_DIR}/aarch64-apple-ios-sim/${REL_TYPE_DIR}/libmatrix_sdk_crypto_ffi.a" \
-output "${GENERATED_DIR}/simulator/libmatrix_sdk_crypto_ffi.a"
# Generate uniffi files
cargo uniffi-bindgen generate \
--language swift \
--lib-file "${TARGET_DIR}/aarch64-apple-ios-sim/${REL_TYPE_DIR}/libmatrix_sdk_crypto_ffi.a" \
--config "${SRC_ROOT}/bindings/${TARGET_CRATE}/uniffi.toml" \
--out-dir ${GENERATED_DIR} \
"${SRC_ROOT}/bindings/${TARGET_CRATE}/src/olm.udl"
# Move headers to the right place
HEADERS_DIR=${GENERATED_DIR}/headers
mkdir -p ${HEADERS_DIR}
mv ${GENERATED_DIR}/*.h ${HEADERS_DIR}
# Rename and move modulemap to the right place
mv ${GENERATED_DIR}/*.modulemap ${HEADERS_DIR}/module.modulemap
# Move source files to the right place
SWIFT_DIR="${GENERATED_DIR}/Sources"
mkdir -p ${SWIFT_DIR}
mv ${GENERATED_DIR}/*.swift ${SWIFT_DIR}
# Build the xcframework
if [ -d "${GENERATED_DIR}/MatrixSDKCryptoFFI.xcframework" ]; then rm -rf "${GENERATED_DIR}/MatrixSDKCryptoFFI.xcframework"; fi
xcodebuild -create-xcframework \
-library "${TARGET_DIR}/aarch64-apple-ios/${REL_TYPE_DIR}/libmatrix_sdk_crypto_ffi.a" \
-headers ${HEADERS_DIR} \
-library "${GENERATED_DIR}/macos/libmatrix_sdk_crypto_ffi.a" \
-headers ${HEADERS_DIR} \
-library "${GENERATED_DIR}/simulator/libmatrix_sdk_crypto_ffi.a" \
-headers ${HEADERS_DIR} \
-output "${GENERATED_DIR}/MatrixSDKCryptoFFI.xcframework"
# Cleanup
if [ -d "${GENERATED_DIR}/macos" ]; then rm -rf "${GENERATED_DIR}/macos"; fi
if [ -d "${GENERATED_DIR}/simulator" ]; then rm -rf "${GENERATED_DIR}/simulator"; fi
if [ -d ${HEADERS_DIR} ]; then rm -rf ${HEADERS_DIR}; fi
# Zip up framework, sources and LICENSE, ready to be uploaded to GitHub Releases and used by MatrixSDKCrypto.podspec
cp ${SRC_ROOT}/LICENSE $GENERATED_DIR
cd $GENERATED_DIR
zip -r MatrixSDKCryptoFFI.zip MatrixSDKCryptoFFI.xcframework Sources LICENSE
rm LICENSE
echo "XCFramework is ready 🚀"
+68
View File
@@ -0,0 +1,68 @@
[package]
name = "matrix-sdk-crypto-ffi"
version = "0.1.0"
authors = ["Damir Jelić <poljar@termina.org.uk>"]
edition = "2021"
rust-version = { workspace = true }
description = "Uniffi based bindings for the Rust SDK crypto crate"
repository = "https://github.com/matrix-org/matrix-rust-sdk"
license = "Apache-2.0"
publish = false
[lib]
crate-type = ["cdylib", "staticlib"]
[features]
default = ["bundled-sqlite"]
bundled-sqlite = ["matrix-sdk-sqlite/bundled"]
[dependencies]
anyhow = { workspace = true }
base64 = { workspace = true }
futures-util = "0.3.25"
hmac = "0.12.1"
http = { workspace = true }
pbkdf2 = "0.11.0"
rand = "0.8.5"
ruma = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
sha2 = "0.10.2"
thiserror = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { version = "0.3.11", features = ["env-filter"] }
# keep in sync with uniffi dependency in matrix-sdk-ffi, and uniffi_bindgen in ffi CI job
uniffi = { workspace = true }
vodozemac = { workspace = true }
zeroize = { workspace = true, features = ["zeroize_derive"] }
[dependencies.js_int]
version = "0.2.2"
features = ["lax_deserialize"]
[dependencies.matrix-sdk-common]
path = "../../crates/matrix-sdk-common"
version = "0.6.0"
[dependencies.matrix-sdk-crypto]
path = "../../crates/matrix-sdk-crypto"
version = "0.6.0"
features = ["qrcode", "backups_v1"]
[dependencies.matrix-sdk-sqlite]
path = "../../crates/matrix-sdk-sqlite"
version = "0.1.0"
default_features = false
features = ["crypto-store"]
[dependencies.tokio]
version = "1.24.2"
default_features = false
features = ["rt-multi-thread"]
[build-dependencies]
uniffi = { workspace = true, features = ["build"] }
[dev-dependencies]
tempfile = "3.3.0"
assert_matches = { workspace = true }
@@ -5,6 +5,8 @@ README mainly describes how to build and integrate the bindings into a Kotlin
based Android project, but the Android specific bits can be skipped if you are
targeting an x86 Linux project.
To build and distribute bindings for iOS projects, see a [dedicated page](../apple/README.md)
## Prerequisites
### Rust
@@ -71,7 +73,7 @@ $ cp ../../target/aarch64-linux-android/debug/libmatrix_crypto.so \
## Minimum Supported Rust Version (MSRV)
These crates are built with the Rust language version 2021 and require a minimum compiler version of `1.60`.
These crates are built with the Rust language version 2021 and require a minimum compiler version of `1.62`.
## License
+3
View File
@@ -0,0 +1,3 @@
fn main() {
uniffi::generate_scaffolding("./src/olm.udl").unwrap();
}
@@ -1,4 +1,4 @@
use std::{collections::HashMap, iter};
use std::{collections::HashMap, iter, ops::DerefMut};
use hmac::Hmac;
use matrix_sdk_crypto::{
@@ -13,12 +13,13 @@ use zeroize::Zeroize;
/// The private part of the backup key, the one used for recovery.
pub struct BackupRecoveryKey {
inner: RecoveryKey,
passphrase_info: Option<PassphraseInfo>,
pub(crate) inner: RecoveryKey,
pub(crate) passphrase_info: Option<PassphraseInfo>,
}
/// Error type for the decryption of backed up room keys.
#[derive(Debug, Error)]
#[derive(Debug, Error, uniffi::Error)]
#[uniffi(flat_error)]
pub enum PkDecryptionError {
/// An internal libolm error happened during decryption.
#[error("Error decryption a PkMessage {0}")]
@@ -26,7 +27,8 @@ pub enum PkDecryptionError {
}
/// Error type for the decoding and storing of the backup key.
#[derive(Debug, Error)]
#[derive(Debug, Error, uniffi::Error)]
#[uniffi(flat_error)]
pub enum DecodeError {
/// An error happened while decoding the recovery key.
#[error(transparent)]
@@ -39,7 +41,7 @@ pub enum DecodeError {
/// Struct containing info about the way the backup key got derived from a
/// passphrase.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, uniffi::Record)]
pub struct PassphraseInfo {
/// The salt that was used during key derivation.
pub private_key_salt: String,
@@ -48,6 +50,7 @@ pub struct PassphraseInfo {
}
/// The public part of the backup key.
#[derive(uniffi::Record)]
pub struct MegolmV1BackupKey {
/// The actual base64 encoded public key.
pub public_key: String,
@@ -59,6 +62,19 @@ pub struct MegolmV1BackupKey {
pub backup_algorithm: String,
}
#[uniffi::export]
impl BackupRecoveryKey {
/// Convert the recovery key to a base 58 encoded string.
pub fn to_base58(&self) -> String {
self.inner.to_base58()
}
/// Convert the recovery key to a base 64 encoded string.
pub fn to_base64(&self) -> String {
self.inner.to_base64()
}
}
impl BackupRecoveryKey {
const KEY_SIZE: usize = 32;
const SALT_SIZE: usize = 32;
@@ -101,7 +117,7 @@ impl BackupRecoveryKey {
let mut key = Box::new([0u8; Self::KEY_SIZE]);
let rounds = rounds as u32;
pbkdf2::<Hmac<Sha512>>(passphrase.as_bytes(), salt.as_bytes(), rounds, &mut *key);
pbkdf2::<Hmac<Sha512>>(passphrase.as_bytes(), salt.as_bytes(), rounds, key.deref_mut());
let recovery_key = RecoveryKey::from_bytes(&key);
@@ -115,7 +131,10 @@ impl BackupRecoveryKey {
}),
}
}
}
#[uniffi::export]
impl BackupRecoveryKey {
/// Get the public part of the backup key.
pub fn megolm_v1_public_key(&self) -> MegolmV1BackupKey {
let public_key = self.inner.megolm_v1_public_key();
@@ -134,16 +153,6 @@ impl BackupRecoveryKey {
}
}
/// Convert the recovery key to a base 58 encoded string.
pub fn to_base58(&self) -> String {
self.inner.to_base58()
}
/// Convert the recovery key to a base 64 encoded string.
pub fn to_base64(&self) -> String {
self.inner.to_base64()
}
/// Try to decrypt a message that was encrypted using the public part of the
/// backup key.
pub fn decrypt_v1(
@@ -3,6 +3,7 @@ use std::collections::HashMap;
use matrix_sdk_crypto::Device as InnerDevice;
/// An E2EE capable Matrix device.
#[derive(uniffi::Record)]
pub struct Device {
/// The device owner.
pub user_id: String,
+121
View File
@@ -0,0 +1,121 @@
#![allow(missing_docs)]
use matrix_sdk_crypto::{
store::CryptoStoreError as InnerStoreError, KeyExportError, MegolmError, OlmError,
SecretImportError as RustSecretImportError, SignatureError as InnerSignatureError,
};
use matrix_sdk_sqlite::OpenStoreError;
use ruma::{IdParseError, OwnedUserId};
#[derive(Debug, thiserror::Error, uniffi::Error)]
#[uniffi(flat_error)]
pub enum KeyImportError {
#[error(transparent)]
Export(#[from] KeyExportError),
#[error(transparent)]
CryptoStore(#[from] InnerStoreError),
#[error(transparent)]
Json(#[from] serde_json::Error),
}
#[derive(Debug, thiserror::Error, uniffi::Error)]
#[uniffi(flat_error)]
pub enum SecretImportError {
#[error(transparent)]
CryptoStore(#[from] InnerStoreError),
#[error(transparent)]
Import(#[from] RustSecretImportError),
}
#[derive(Debug, thiserror::Error, uniffi::Error)]
#[uniffi(flat_error)]
pub enum SignatureError {
#[error(transparent)]
Signature(#[from] InnerSignatureError),
#[error(transparent)]
Identifier(#[from] IdParseError),
#[error(transparent)]
CryptoStore(#[from] InnerStoreError),
#[error("Unknown device {0} {1}")]
UnknownDevice(OwnedUserId, String),
#[error("Unknown user identity {0}")]
UnknownUserIdentity(String),
}
#[derive(Debug, thiserror::Error)]
pub enum CryptoStoreError {
#[error("Failed to open the store")]
OpenStore(#[from] OpenStoreError),
#[error(transparent)]
CryptoStore(#[from] InnerStoreError),
#[error(transparent)]
OlmError(#[from] OlmError),
#[error(transparent)]
Serialization(#[from] serde_json::Error),
#[error("The given string is not a valid user ID: source {0}, error {1}")]
InvalidUserId(String, IdParseError),
#[error(transparent)]
Identifier(#[from] IdParseError),
}
#[derive(Debug, uniffi::Error)]
pub enum DecryptionError {
Serialization { error: String },
Identifier { error: String },
Megolm { error: String },
MissingRoomKey { error: String, withheld_code: Option<String> },
Store { error: String },
}
impl From<MegolmError> for DecryptionError {
fn from(value: MegolmError) -> Self {
match value {
MegolmError::MissingRoomKey(withheld_code) => Self::MissingRoomKey {
error: "Withheld Inbound group session".to_owned(),
withheld_code: withheld_code.map(|w| w.as_str().to_owned()),
},
_ => Self::Megolm { error: value.to_string() },
}
}
}
impl From<serde_json::Error> for DecryptionError {
fn from(err: serde_json::Error) -> Self {
Self::Serialization { error: err.to_string() }
}
}
impl From<IdParseError> for DecryptionError {
fn from(err: IdParseError) -> Self {
Self::Identifier { error: err.to_string() }
}
}
impl From<InnerStoreError> for DecryptionError {
fn from(err: InnerStoreError) -> Self {
Self::Store { error: err.to_string() }
}
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use super::*;
#[test]
fn test_withheld_error_mapping() {
use matrix_sdk_crypto::types::events::room_key_withheld::WithheldCode;
let inner_error = MegolmError::MissingRoomKey(Some(WithheldCode::Unverified));
let binding_error: DecryptionError = inner_error.into();
let code = assert_matches!(
binding_error,
DecryptionError::MissingRoomKey { error: _, withheld_code: Some(withheld_code) } => withheld_code
);
assert_eq!("m.unverified", code)
}
}
@@ -4,6 +4,7 @@
//! client or client library in any of the language targets Uniffi supports.
#![warn(missing_docs)]
#![allow(unused_qualifications)]
mod backup_recovery_key;
mod device;
@@ -14,8 +15,9 @@ mod responses;
mod users;
mod verification;
use std::{collections::HashMap, convert::TryFrom, str::FromStr, sync::Arc};
use std::{borrow::Borrow, collections::HashMap, str::FromStr, sync::Arc, time::Duration};
use anyhow::Context;
pub use backup_recovery_key::{
BackupRecoveryKey, DecodeError, MegolmV1BackupKey, PassphraseInfo, PkDecryptionError,
};
@@ -25,21 +27,36 @@ pub use error::{
};
use js_int::UInt;
pub use logger::{set_logger, Logger};
pub use machine::{KeyRequestPair, OlmMachine};
pub use machine::{KeyRequestPair, OlmMachine, SignatureVerification};
use matrix_sdk_common::deserialized_responses::ShieldState as RustShieldState;
use matrix_sdk_crypto::{
backups::SignatureState,
olm::{IdentityKeys, InboundGroupSession, Session},
store::{Changes, CryptoStore, RoomSettings as RustRoomSettings},
types::{EventEncryptionAlgorithm as RustEventEncryptionAlgorithm, SigningKey},
EncryptionSettings as RustEncryptionSettings, LocalTrust,
};
use matrix_sdk_sqlite::SqliteCryptoStore;
pub use responses::{
BootstrapCrossSigningResult, DeviceLists, KeysImportResult, OutgoingVerificationRequest,
Request, RequestType, SignatureUploadRequest, UploadSigningKeysRequest,
};
use ruma::{DeviceId, DeviceKeyAlgorithm, OwnedUserId, RoomId, SecondsSinceUnixEpoch, UserId};
use ruma::{
events::room::history_visibility::HistoryVisibility as RustHistoryVisibility, DeviceId,
DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, RoomId, SecondsSinceUnixEpoch, UserId,
};
use serde::{Deserialize, Serialize};
use tokio::runtime::Runtime;
pub use users::UserIdentity;
pub use verification::{
CancelInfo, ConfirmVerificationResult, QrCode, RequestVerificationResult, Sas, ScanResult,
StartSasResult, Verification, VerificationRequest,
CancelInfo, ConfirmVerificationResult, QrCode, QrCodeListener, QrCodeState,
RequestVerificationResult, Sas, SasListener, SasState, ScanResult, StartSasResult,
Verification, VerificationRequest, VerificationRequestListener, VerificationRequestState,
};
use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
/// Struct collecting data that is important to migrate to the rust-sdk
#[derive(Deserialize, Serialize)]
#[derive(Deserialize, Serialize, uniffi::Record)]
pub struct MigrationData {
/// The pickled version of the Olm Account
account: PickledAccount,
@@ -48,7 +65,7 @@ pub struct MigrationData {
/// The list of Megolm inbound group sessions.
inbound_group_sessions: Vec<PickledInboundGroupSession>,
/// The Olm pickle key that was used to pickle all the Olm objects.
pickle_key: String,
pickle_key: Vec<u8>,
/// The backup version that is currently active.
backup_version: Option<String>,
// The backup recovery key, as a base58 encoded string.
@@ -57,17 +74,38 @@ pub struct MigrationData {
cross_signing: CrossSigningKeyExport,
/// The list of users that the Rust SDK should track.
tracked_users: Vec<String>,
/// Map of room settings
room_settings: HashMap<String, RoomSettings>,
}
/// Struct collecting data that is important to migrate sessions to the rust-sdk
#[derive(uniffi::Record)]
pub struct SessionMigrationData {
/// The user id that the data belongs to.
user_id: String,
/// The device id that the data belongs to.
device_id: String,
/// The Curve25519 public key of the Account that owns this data.
curve25519_key: String,
/// The Ed25519 public key of the Account that owns this data.
ed25519_key: String,
/// The list of pickleds Olm Sessions.
sessions: Vec<PickledSession>,
/// The list of pickled Megolm inbound group sessions.
inbound_group_sessions: Vec<PickledInboundGroupSession>,
/// The Olm pickle key that was used to pickle all the Olm objects.
pickle_key: Vec<u8>,
}
/// A pickled version of an `Account`.
///
/// Holds all the information that needs to be stored in a database to restore
/// an account.
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, uniffi::Record)]
pub struct PickledAccount {
/// The user id of the account owner.
pub user_id: String,
/// The device id of the account owner.
/// The device ID of the account owner.
pub device_id: String,
/// The pickled version of the Olm account.
pub pickle: String,
@@ -81,7 +119,7 @@ pub struct PickledAccount {
///
/// Holds all the information that needs to be stored in a database to restore
/// a Session.
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, uniffi::Record)]
pub struct PickledSession {
/// The pickle string holding the Olm Session.
pub pickle: String,
@@ -99,7 +137,7 @@ pub struct PickledSession {
///
/// Holds all the information that needs to be stored in a database to restore
/// an InboundGroupSession.
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, uniffi::Record)]
pub struct PickledInboundGroupSession {
/// The pickle string holding the InboundGroupSession.
pub pickle: String,
@@ -120,7 +158,7 @@ pub struct PickledInboundGroupSession {
}
/// Error type for the migration process.
#[derive(thiserror::Error, Debug)]
#[derive(Debug, thiserror::Error, uniffi::Error)]
pub enum MigrationError {
/// Generic catch all error variant.
#[error("error migrating database: {error_message}")]
@@ -136,38 +174,43 @@ impl From<anyhow::Error> for MigrationError {
}
}
/// Migrate a libolm based setup to a vodozemac based setup stored in a Sled
/// Migrate a libolm based setup to a vodozemac based setup stored in a SQLite
/// store.
///
/// # Arguments
///
/// * `data` - The data that should be migrated over to the Sled store.
/// * `data` - The data that should be migrated over to the SQLite store.
///
/// * `path` - The path where the Sled store should be created.
/// * `path` - The path where the SQLite store should be created.
///
/// * `passphrase` - The passphrase that should be used to encrypt the data at
/// rest in the Sled store. **Warning**, if no passphrase is given, the store
/// rest in the SQLite store. **Warning**, if no passphrase is given, the store
/// and all its data will remain unencrypted.
///
/// * `progress_listener` - A callback that can be used to introspect the
/// progress of the migration.
#[uniffi::export]
pub fn migrate(
data: MigrationData,
path: String,
passphrase: Option<String>,
progress_listener: Box<dyn ProgressListener>,
) -> Result<(), MigrationError> {
let runtime = Runtime::new().context("initializing tokio runtime")?;
runtime.block_on(async move {
migrate_data(data, &path, passphrase, progress_listener).await?;
Ok(())
})
}
async fn migrate_data(
mut data: MigrationData,
path: &str,
passphrase: Option<String>,
progress_listener: Box<dyn ProgressListener>,
) -> anyhow::Result<()> {
use matrix_sdk_crypto::{
olm::PrivateCrossSigningIdentity,
store::{Changes as RustChanges, CryptoStore, RecoveryKey},
};
use matrix_sdk_sled::CryptoStore as SledStore;
use tokio::runtime::Runtime;
use vodozemac::{
megolm::InboundGroupSession,
olm::{Account, Session},
Curve25519PublicKey,
};
use matrix_sdk_crypto::{olm::PrivateCrossSigningIdentity, store::RecoveryKey};
use vodozemac::olm::Account;
use zeroize::Zeroize;
// The total steps here include all the sessions/inbound group sessions and
@@ -184,13 +227,17 @@ pub fn migrate(
progress_listener.on_progress(progress as i32, total as i32)
};
let store = SledStore::open_with_passphrase(path, passphrase.as_deref())?;
let runtime = Runtime::new()?;
let store = SqliteCryptoStore::open(path, passphrase.as_deref()).await?;
processed_steps += 1;
listener(processed_steps, total_steps);
let user_id: Arc<UserId> = (&*parse_user_id(&data.account.user_id)?).into();
let user_id: Arc<UserId> = {
let user_id: OwnedUserId = parse_user_id(&data.account.user_id)?;
let user_id: &UserId = user_id.borrow();
user_id.into()
};
let device_id: Box<DeviceId> = data.account.device_id.into();
let device_id: Arc<DeviceId> = device_id.into();
@@ -209,72 +256,29 @@ pub fn migrate(
processed_steps += 1;
listener(processed_steps, total_steps);
let mut sessions = Vec::new();
for session_pickle in data.sessions {
let pickle =
Session::from_libolm_pickle(&session_pickle.pickle, &data.pickle_key)?.pickle();
let creation_time = SecondsSinceUnixEpoch(UInt::from_str(&session_pickle.creation_time)?);
let last_use_time = SecondsSinceUnixEpoch(UInt::from_str(&session_pickle.last_use_time)?);
let pickle = matrix_sdk_crypto::olm::PickledSession {
pickle,
sender_key: Curve25519PublicKey::from_base64(&session_pickle.sender_key)?,
created_using_fallback_key: session_pickle.created_using_fallback_key,
creation_time,
last_use_time,
};
let session = matrix_sdk_crypto::olm::Session::from_pickle(
user_id.clone(),
device_id.clone(),
identity_keys.clone(),
pickle,
);
sessions.push(session);
processed_steps += 1;
listener(processed_steps, total_steps);
}
let mut inbound_group_sessions = Vec::new();
for session in data.inbound_group_sessions {
let pickle =
InboundGroupSession::from_libolm_pickle(&session.pickle, &data.pickle_key)?.pickle();
let pickle = matrix_sdk_crypto::olm::PickledInboundGroupSession {
pickle,
sender_key: session.sender_key,
signing_key: session
.signing_key
.into_iter()
.map(|(k, v)| Ok((DeviceKeyAlgorithm::try_from(k)?, v)))
.collect::<anyhow::Result<_>>()?,
room_id: RoomId::parse(session.room_id)?,
forwarding_chains: session.forwarding_chains,
imported: session.imported,
backed_up: session.backed_up,
history_visibility: None,
};
let session = matrix_sdk_crypto::olm::InboundGroupSession::from_pickle(pickle)?;
inbound_group_sessions.push(session);
processed_steps += 1;
listener(processed_steps, total_steps);
}
let (sessions, inbound_group_sessions) = collect_sessions(
processed_steps,
total_steps,
&listener,
&data.pickle_key,
user_id.clone(),
device_id,
identity_keys,
data.sessions,
data.inbound_group_sessions,
)?;
let recovery_key =
data.backup_recovery_key.map(|k| RecoveryKey::from_base58(k.as_str())).transpose()?;
let cross_signing = PrivateCrossSigningIdentity::empty((*user_id).into());
runtime.block_on(cross_signing.import_secrets_unchecked(
data.cross_signing.master_key.as_deref(),
data.cross_signing.self_signing_key.as_deref(),
data.cross_signing.user_signing_key.as_deref(),
))?;
cross_signing
.import_secrets_unchecked(
data.cross_signing.master_key.as_deref(),
data.cross_signing.self_signing_key.as_deref(),
data.cross_signing.user_signing_key.as_deref(),
)
.await?;
data.cross_signing.master_key.zeroize();
data.cross_signing.self_signing_key.zeroize();
@@ -290,22 +294,39 @@ pub fn migrate(
.collect::<anyhow::Result<_>>()?;
let tracked_users: Vec<_> = tracked_users.iter().map(|(u, d)| (&**u, *d)).collect();
runtime.block_on(store.save_tracked_users(tracked_users.as_slice()))?;
store.save_tracked_users(tracked_users.as_slice()).await?;
processed_steps += 1;
listener(processed_steps, total_steps);
let changes = RustChanges {
let mut room_settings = HashMap::new();
for (room_id, settings) in data.room_settings {
let room_id = RoomId::parse(room_id)?;
room_settings.insert(room_id, settings.into());
}
let changes = Changes {
account: Some(account),
private_identity: Some(cross_signing),
sessions,
inbound_group_sessions,
recovery_key,
backup_version: data.backup_version,
room_settings,
..Default::default()
};
runtime.block_on(store.save_changes(changes))?;
save_changes(processed_steps, total_steps, &listener, changes, &store).await
}
async fn save_changes(
mut processed_steps: usize,
total_steps: usize,
listener: &dyn Fn(usize, usize),
changes: Changes,
store: &SqliteCryptoStore,
) -> anyhow::Result<()> {
store.save_changes(changes).await?;
processed_steps += 1;
listener(processed_steps, total_steps);
@@ -313,6 +334,197 @@ pub fn migrate(
Ok(())
}
/// Migrate sessions and group sessions of a libolm based setup to a vodozemac
/// based setup stored in a SQLite store.
///
/// This method allows you to migrate a subset of the data, it should only be
/// used after the [`migrate()`] method has been already used.
///
/// # Arguments
///
/// * `data` - The data that should be migrated over to the SQLite store.
///
/// * `path` - The path where the SQLite store should be created.
///
/// * `passphrase` - The passphrase that should be used to encrypt the data at
/// rest in the SQLite store. **Warning**, if no passphrase is given, the store
/// and all its data will remain unencrypted.
///
/// * `progress_listener` - A callback that can be used to introspect the
/// progress of the migration.
#[uniffi::export]
pub fn migrate_sessions(
data: SessionMigrationData,
path: String,
passphrase: Option<String>,
progress_listener: Box<dyn ProgressListener>,
) -> Result<(), MigrationError> {
let runtime = Runtime::new().context("initializing tokio runtime")?;
runtime.block_on(migrate_session_data(data, &path, passphrase, progress_listener))?;
Ok(())
}
async fn migrate_session_data(
data: SessionMigrationData,
path: &str,
passphrase: Option<String>,
progress_listener: Box<dyn ProgressListener>,
) -> anyhow::Result<()> {
let store = SqliteCryptoStore::open(path, passphrase.as_deref()).await?;
let listener = |progress: usize, total: usize| {
progress_listener.on_progress(progress as i32, total as i32)
};
let total_steps = 1 + data.sessions.len() + data.inbound_group_sessions.len();
let processed_steps = 0;
let user_id = UserId::parse(data.user_id)?.into();
let device_id: OwnedDeviceId = data.device_id.into();
let identity_keys = IdentityKeys {
ed25519: Ed25519PublicKey::from_base64(&data.ed25519_key)?,
curve25519: Curve25519PublicKey::from_base64(&data.curve25519_key)?,
}
.into();
let (sessions, inbound_group_sessions) = collect_sessions(
processed_steps,
total_steps,
&listener,
&data.pickle_key,
user_id,
device_id.into(),
identity_keys,
data.sessions,
data.inbound_group_sessions,
)?;
let changes = Changes { sessions, inbound_group_sessions, ..Default::default() };
save_changes(processed_steps, total_steps, &listener, changes, &store).await
}
#[allow(clippy::too_many_arguments)]
fn collect_sessions(
mut processed_steps: usize,
total_steps: usize,
listener: &dyn Fn(usize, usize),
pickle_key: &[u8],
user_id: Arc<UserId>,
device_id: Arc<DeviceId>,
identity_keys: Arc<IdentityKeys>,
session_pickles: Vec<PickledSession>,
group_session_pickles: Vec<PickledInboundGroupSession>,
) -> anyhow::Result<(Vec<Session>, Vec<InboundGroupSession>)> {
let mut sessions = Vec::new();
for session_pickle in session_pickles {
let pickle =
vodozemac::olm::Session::from_libolm_pickle(&session_pickle.pickle, pickle_key)?
.pickle();
let creation_time = SecondsSinceUnixEpoch(UInt::from_str(&session_pickle.creation_time)?);
let last_use_time = SecondsSinceUnixEpoch(UInt::from_str(&session_pickle.last_use_time)?);
let pickle = matrix_sdk_crypto::olm::PickledSession {
pickle,
sender_key: Curve25519PublicKey::from_base64(&session_pickle.sender_key)?,
created_using_fallback_key: session_pickle.created_using_fallback_key,
creation_time,
last_use_time,
};
let session =
Session::from_pickle(user_id.clone(), device_id.clone(), identity_keys.clone(), pickle);
sessions.push(session);
processed_steps += 1;
listener(processed_steps, total_steps);
}
let mut inbound_group_sessions = Vec::new();
for session in group_session_pickles {
let pickle = vodozemac::megolm::InboundGroupSession::from_libolm_pickle(
&session.pickle,
pickle_key,
)?
.pickle();
let sender_key = Curve25519PublicKey::from_base64(&session.sender_key)?;
let pickle = matrix_sdk_crypto::olm::PickledInboundGroupSession {
pickle,
sender_key,
signing_key: session
.signing_key
.into_iter()
.map(|(k, v)| {
let algorithm = DeviceKeyAlgorithm::try_from(k)?;
let key = SigningKey::from_parts(&algorithm, v)?;
Ok((algorithm, key))
})
.collect::<anyhow::Result<_>>()?,
room_id: RoomId::parse(session.room_id)?,
imported: session.imported,
backed_up: session.backed_up,
history_visibility: None,
algorithm: RustEventEncryptionAlgorithm::MegolmV1AesSha2,
};
let session = matrix_sdk_crypto::olm::InboundGroupSession::from_pickle(pickle)?;
inbound_group_sessions.push(session);
processed_steps += 1;
listener(processed_steps, total_steps);
}
Ok((sessions, inbound_group_sessions))
}
/// Migrate room settings, including room algorithm and whether to block
/// untrusted devices from legacy store to Sqlite store.
///
/// Note that this method should only be used if a client has already migrated
/// account data via [migrate](#method.migrate) method, which did not include
/// room settings. For a brand new migration, the [migrate](#method.migrate)
/// method will take care of room settings automatically, if provided.
///
/// # Arguments
///
/// * `room_settings` - Map of room settings
///
/// * `path` - The path where the Sqlite store should be created.
///
/// * `passphrase` - The passphrase that should be used to encrypt the data at
/// rest in the Sqlite store. **Warning**, if no passphrase is given, the store
/// and all its data will remain unencrypted.
#[uniffi::export]
pub fn migrate_room_settings(
room_settings: HashMap<String, RoomSettings>,
path: String,
passphrase: Option<String>,
) -> Result<(), MigrationError> {
let runtime = Runtime::new().context("initializing tokio runtime")?;
runtime.block_on(async move {
let store = SqliteCryptoStore::open(path, passphrase.as_deref())
.await
.context("opening sqlite crypto store")?;
let mut rust_settings = HashMap::new();
for (room_id, settings) in room_settings {
let room_id = RoomId::parse(room_id).context("parsing room ID")?;
rust_settings.insert(room_id, settings.into());
}
let changes = Changes { room_settings: rust_settings, ..Default::default() };
store.save_changes(changes).await.context("saving changes")?;
Ok(())
})
}
/// Callback that will be passed over the FFI to report progress
pub trait ProgressListener {
/// The callback that should be called on the Rust side
@@ -331,7 +543,114 @@ impl<T: Fn(i32, i32)> ProgressListener for T {
}
}
/// An encryption algorithm to be used to encrypt messages sent to a room.
#[derive(Debug, Deserialize, Serialize, PartialEq, uniffi::Enum)]
pub enum EventEncryptionAlgorithm {
/// Olm version 1 using Curve25519, AES-256, and SHA-256.
OlmV1Curve25519AesSha2,
/// Megolm version 1 using AES-256 and SHA-256.
MegolmV1AesSha2,
}
impl From<EventEncryptionAlgorithm> for RustEventEncryptionAlgorithm {
fn from(a: EventEncryptionAlgorithm) -> Self {
match a {
EventEncryptionAlgorithm::OlmV1Curve25519AesSha2 => Self::OlmV1Curve25519AesSha2,
EventEncryptionAlgorithm::MegolmV1AesSha2 => Self::MegolmV1AesSha2,
}
}
}
impl TryFrom<RustEventEncryptionAlgorithm> for EventEncryptionAlgorithm {
type Error = serde_json::Error;
fn try_from(value: RustEventEncryptionAlgorithm) -> Result<Self, Self::Error> {
match value {
RustEventEncryptionAlgorithm::OlmV1Curve25519AesSha2 => {
Ok(Self::OlmV1Curve25519AesSha2)
}
RustEventEncryptionAlgorithm::MegolmV1AesSha2 => Ok(Self::MegolmV1AesSha2),
_ => Err(serde::de::Error::custom(format!("Unsupported algorithm {value}"))),
}
}
}
/// Who can see a room's history.
#[derive(uniffi::Enum)]
pub enum HistoryVisibility {
/// Previous events are accessible to newly joined members from the point
/// they were invited onwards.
///
/// Events stop being accessible when the member's state changes to
/// something other than *invite* or *join*.
Invited,
/// Previous events are accessible to newly joined members from the point
/// they joined the room onwards.
/// Events stop being accessible when the member's state changes to
/// something other than *join*.
Joined,
/// Previous events are always accessible to newly joined members.
///
/// All events in the room are accessible, even those sent when the member
/// was not a part of the room.
Shared,
/// All events while this is the `HistoryVisibility` value may be shared by
/// any participating homeserver with anyone, regardless of whether they
/// have ever joined the room.
WorldReadable,
}
impl From<HistoryVisibility> for RustHistoryVisibility {
fn from(h: HistoryVisibility) -> Self {
match h {
HistoryVisibility::Invited => Self::Invited,
HistoryVisibility::Joined => Self::Joined,
HistoryVisibility::Shared => Self::Shared,
HistoryVisibility::WorldReadable => Self::Shared,
}
}
}
/// Settings that should be used when a room key is shared.
///
/// These settings control which algorithm the room key should use, how long a
/// room key should be used and some other important information that determines
/// the lifetime of a room key.
#[derive(uniffi::Record)]
pub struct EncryptionSettings {
/// The encryption algorithm that should be used in the room.
pub algorithm: EventEncryptionAlgorithm,
/// How long can the room key be used before it should be rotated. Time in
/// seconds.
pub rotation_period: u64,
/// How many messages should be sent before the room key should be rotated.
pub rotation_period_msgs: u64,
/// The current history visibility of the room. The visibility will be
/// tracked by the room key and the key will be rotated if the visibility
/// changes.
pub history_visibility: HistoryVisibility,
/// Should untrusted devices receive the room key, or should they be
/// excluded from the conversation.
pub only_allow_trusted_devices: bool,
}
impl From<EncryptionSettings> for RustEncryptionSettings {
fn from(v: EncryptionSettings) -> Self {
RustEncryptionSettings {
algorithm: v.algorithm.into(),
rotation_period: Duration::from_secs(v.rotation_period),
rotation_period_msgs: v.rotation_period_msgs,
history_visibility: v.history_visibility.into(),
only_allow_trusted_devices: v.only_allow_trusted_devices,
}
}
}
/// An event that was successfully decrypted.
#[derive(uniffi::Record)]
pub struct DecryptedEvent {
/// The decrypted version of the event.
pub clear_event: String,
@@ -343,11 +662,53 @@ pub struct DecryptedEvent {
/// key to us. Is empty if the key came directly from the sender of the
/// event.
pub forwarding_curve25519_chain: Vec<String>,
/// The shield state (color and message to display to user) for the event,
/// representing the event's authenticity. Computed from the properties of
/// the sender user identity and their Olm device.
///
/// Note that this is computed at time of decryption, so the value reflects
/// the computed event authenticity at that time. Authenticity-related
/// properties can change later on, such as when a user identity is
/// subsequently verified or a device is deleted.
pub shield_state: ShieldState,
}
/// Take a look at [`matrix_sdk_common::deserialized_responses::ShieldState`]
/// for more info.
#[allow(missing_docs)]
#[derive(uniffi::Enum)]
pub enum ShieldColor {
Red,
Grey,
None,
}
/// Take a look at [`matrix_sdk_common::deserialized_responses::ShieldState`]
/// for more info.
#[derive(uniffi::Record)]
#[allow(missing_docs)]
pub struct ShieldState {
color: ShieldColor,
message: Option<String>,
}
impl From<RustShieldState> for ShieldState {
fn from(value: RustShieldState) -> Self {
match value {
RustShieldState::Red { message } => {
Self { color: ShieldColor::Red, message: Some(message.to_owned()) }
}
RustShieldState::Grey { message } => {
Self { color: ShieldColor::Grey, message: Some(message.to_owned()) }
}
RustShieldState::None => Self { color: ShieldColor::None, message: None },
}
}
}
/// Struct representing the state of our private cross signing keys, it shows
/// which private cross signing keys we have locally stored.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, uniffi::Record)]
pub struct CrossSigningStatus {
/// Do we have the master key.
pub has_master: bool,
@@ -361,7 +722,7 @@ pub struct CrossSigningStatus {
/// A struct containing private cross signing keys that can be backed up or
/// uploaded to the secret store.
#[derive(Deserialize, Serialize)]
#[derive(Deserialize, Serialize, uniffi::Record)]
pub struct CrossSigningKeyExport {
/// The seed of the master key encoded as unpadded base64.
pub master_key: Option<String>,
@@ -372,6 +733,7 @@ pub struct CrossSigningKeyExport {
}
/// Struct holding the number of room keys we have.
#[derive(uniffi::Record)]
pub struct RoomKeyCounts {
/// The total number of room keys.
pub total: i64,
@@ -380,11 +742,25 @@ pub struct RoomKeyCounts {
}
/// Backup keys and information we load from the store.
#[derive(uniffi::Object)]
pub struct BackupKeys {
/// The recovery key as a base64 encoded string.
pub recovery_key: String,
recovery_key: Arc<BackupRecoveryKey>,
/// The version that is used with the recovery key.
pub backup_version: String,
backup_version: String,
}
#[uniffi::export]
impl BackupKeys {
/// Get the recovery key that we're holding on to.
pub fn recovery_key(&self) -> Arc<BackupRecoveryKey> {
self.recovery_key.clone()
}
/// Get the backups version that we're holding on to.
pub fn backup_version(&self) -> String {
self.backup_version.to_owned()
}
}
impl TryFrom<matrix_sdk_crypto::store::BackupKeys> for BackupKeys {
@@ -392,7 +768,11 @@ impl TryFrom<matrix_sdk_crypto::store::BackupKeys> for BackupKeys {
fn try_from(keys: matrix_sdk_crypto::store::BackupKeys) -> Result<Self, Self::Error> {
Ok(Self {
recovery_key: keys.recovery_key.ok_or(())?.to_base64(),
recovery_key: BackupRecoveryKey {
inner: keys.recovery_key.ok_or(())?,
passphrase_info: None,
}
.into(),
backup_version: keys.backup_version.ok_or(())?,
})
}
@@ -434,17 +814,49 @@ impl From<matrix_sdk_crypto::CrossSigningStatus> for CrossSigningStatus {
}
}
/// Room encryption settings which are modified by state events or user options
#[derive(Debug, PartialEq, Deserialize, Serialize, uniffi::Record)]
pub struct RoomSettings {
/// The encryption algorithm that should be used in the room.
pub algorithm: EventEncryptionAlgorithm,
/// Should untrusted devices receive the room key, or should they be
/// excluded from the conversation.
pub only_allow_trusted_devices: bool,
}
impl TryFrom<RustRoomSettings> for RoomSettings {
type Error = serde_json::Error;
fn try_from(value: RustRoomSettings) -> Result<Self, Self::Error> {
let algorithm = value.algorithm.try_into()?;
Ok(Self { algorithm, only_allow_trusted_devices: value.only_allow_trusted_devices })
}
}
impl From<RoomSettings> for RustRoomSettings {
fn from(value: RoomSettings) -> Self {
Self {
algorithm: value.algorithm.into(),
only_allow_trusted_devices: value.only_allow_trusted_devices,
}
}
}
fn parse_user_id(user_id: &str) -> Result<OwnedUserId, CryptoStoreError> {
ruma::UserId::parse(user_id).map_err(|e| CryptoStoreError::InvalidUserId(user_id.to_owned(), e))
}
#[allow(warnings)]
mod generated {
use super::*;
include!(concat!(env!("OUT_DIR"), "/olm.uniffi.rs"));
#[uniffi::export]
fn version() -> String {
matrix_sdk_crypto::VERSION.to_owned()
}
pub use generated::*;
#[uniffi::export]
fn vodozemac_version() -> String {
vodozemac::VERSION.to_owned()
}
uniffi::include_scaffolding!("olm");
#[cfg(test)]
mod test {
@@ -453,7 +865,7 @@ mod test {
use tempfile::tempdir;
use super::MigrationData;
use crate::{migrate, OlmMachine};
use crate::{migrate, EventEncryptionAlgorithm, OlmMachine, RoomSettings};
#[test]
fn android_migration() -> Result<()> {
@@ -521,7 +933,9 @@ mod test {
"backed_up":true
}
],
"pickle_key":"\u{0011}$xJ_N8$>{\u{0005}iJoF03eBVt\u{000e}rUU\\,GYc7J",
"pickle_key": [17, 36, 120, 74, 95, 78, 56, 36, 62, 123, 5, 105, 74,
111, 70, 48, 51, 101, 66, 86, 116, 14, 114, 85, 85,
92, 44, 71, 89, 99, 55, 74],
"backup_version":"3",
"backup_recovery_key":"EsTHScmRV5oT1WBhe2mj2Gn3odeYantZ4NEk7L51p6L8hrmB",
"cross_signing":{
@@ -534,25 +948,38 @@ mod test {
"@this-is-me:matrix.org",
"@Amandine:matrix.org",
"@ganfra:matrix.org"
]
],
"room_settings": {
"!AZkqtjvtwPAuyNOXEt:matrix.org": {
"algorithm": "OlmV1Curve25519AesSha2",
"only_allow_trusted_devices": true
},
"!CWLUCoEWXSFyTCOtfL:matrix.org": {
"algorithm": "MegolmV1AesSha2",
"only_allow_trusted_devices": false
},
}
});
let migration_data: MigrationData = serde_json::from_value(data)?;
let dir = tempdir()?;
let path =
dir.path().to_str().expect("Creating a string from the tempdir path should not fail");
let path = dir
.path()
.to_str()
.expect("Creating a string from the tempdir path should not fail")
.to_owned();
migrate(migration_data, path, None, Box::new(|_, _| {}))?;
migrate(migration_data, path.clone(), None, Box::new(|_, _| {}))?;
let machine = OlmMachine::new("@ganfra146:matrix.org", "DEWRCMENGS", path, None)?;
let machine = OlmMachine::new("@ganfra146:matrix.org", "DEWRCMENGS", &path, None)?;
assert_eq!(
machine.identity_keys()["ed25519"],
"JGgPQRuYj3ScMdPS+A0P+k/1qS9Hr3qeKXLscI+hS78"
);
let room_keys = machine.runtime.block_on(machine.inner.export_keys(|_| true))?;
let room_keys = machine.runtime.block_on(machine.inner.export_room_keys(|_| true))?;
assert_eq!(room_keys.len(), 2);
let cross_signing_status = machine.cross_signing_status();
@@ -563,6 +990,27 @@ mod test {
let backup_keys = machine.get_backup_keys()?;
assert!(backup_keys.is_some());
let settings1 = machine.get_room_settings("!AZkqtjvtwPAuyNOXEt:matrix.org".into())?;
assert_eq!(
Some(RoomSettings {
algorithm: EventEncryptionAlgorithm::OlmV1Curve25519AesSha2,
only_allow_trusted_devices: true
}),
settings1
);
let settings2 = machine.get_room_settings("!CWLUCoEWXSFyTCOtfL:matrix.org".into())?;
assert_eq!(
Some(RoomSettings {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
only_allow_trusted_devices: false
}),
settings2
);
let settings3 = machine.get_room_settings("!XYZ:matrix.org".into())?;
assert!(settings3.is_none());
Ok(())
}
}
@@ -41,16 +41,22 @@ pub struct LoggerWrapper {
}
/// Set the logger that should be used to forward Rust logs over FFI.
#[uniffi::export]
pub fn set_logger(logger: Box<dyn Logger>) {
let logger = LoggerWrapper { inner: Arc::new(Mutex::new(logger)) };
let filter = EnvFilter::from_default_env().add_directive(
"matrix_sdk_crypto=trace".parse().expect("Can't parse logging filter directive"),
);
let filter = EnvFilter::from_default_env()
.add_directive(
"matrix_sdk_crypto=trace".parse().expect("Can't parse logging filter directive"),
)
.add_directive(
"matrix_sdk_sqlite=debug".parse().expect("Can't parse logging filter directive"),
);
let _ = tracing_subscriber::fmt()
.with_writer(logger)
.with_env_filter(filter)
.with_ansi(false)
.without_time()
.try_init();
}
+101
View File
@@ -0,0 +1,101 @@
namespace matrix_sdk_crypto_ffi {};
callback interface Logger {
void log(string log_line);
};
callback interface ProgressListener {
void on_progress(i32 progress, i32 total);
};
[Error]
enum CryptoStoreError {
"OpenStore",
"CryptoStore",
"OlmError",
"Serialization",
"InvalidUserId",
"Identifier",
};
dictionary CancelInfo {
string cancel_code;
string reason;
boolean cancelled_by_us;
};
[Enum]
interface SasState {
Started();
Accepted();
KeysExchanged(sequence<i32>? emojis, sequence<i32> decimals);
Confirmed();
Done();
Cancelled(CancelInfo cancel_info);
};
callback interface SasListener {
void on_change(SasState state);
};
[Enum]
interface QrCodeState {
Started();
Scanned();
Confirmed();
Reciprocated();
Done();
Cancelled(CancelInfo cancel_info);
};
callback interface QrCodeListener {
void on_change(QrCodeState state);
};
[Enum]
interface VerificationRequestState {
Requested();
Ready(sequence<string> their_methods, sequence<string> our_methods);
Done();
Cancelled(CancelInfo cancel_info);
};
callback interface VerificationRequestListener {
void on_change(VerificationRequestState state);
};
enum LocalTrust {
"Verified",
"BlackListed",
"Ignored",
"Unset",
};
interface OlmMachine {
[Throws=CryptoStoreError]
constructor(
[ByRef] string user_id,
[ByRef] string device_id,
[ByRef] string path,
string? passphrase
);
};
enum SignatureState {
"Missing",
"Invalid",
"ValidButNotTrusted",
"ValidAndTrusted",
};
interface BackupRecoveryKey {
constructor();
[Name=from_passphrase]
constructor(string passphrase, string salt, i32 rounds);
[Name=new_from_passphrase]
constructor(string passphrase);
[Name=from_base64, Throws=DecodeError]
constructor(string key);
[Name=from_base58, Throws=DecodeError]
constructor(string key);
};
@@ -19,7 +19,7 @@ use ruma::{
},
},
message::send_message_event::v3::Response as RoomMessageResponse,
sync::sync_events::v3::DeviceLists as RumaDeviceLists,
sync::sync_events::DeviceLists as RumaDeviceLists,
to_device::send_event_to_device::v3::Response as ToDeviceResponse,
},
assign,
@@ -28,6 +28,7 @@ use ruma::{
};
use serde_json::json;
#[derive(uniffi::Record)]
pub struct SignatureUploadRequest {
pub body: String,
}
@@ -41,6 +42,7 @@ impl From<RustSignatureUploadRequest> for SignatureUploadRequest {
}
}
#[derive(uniffi::Record)]
pub struct UploadSigningKeysRequest {
pub master_key: String,
pub self_signing_key: String,
@@ -66,6 +68,7 @@ impl From<RustUploadSigningKeysRequest> for UploadSigningKeysRequest {
}
}
#[derive(uniffi::Record)]
pub struct BootstrapCrossSigningResult {
pub upload_signing_keys_request: UploadSigningKeysRequest,
pub signature_request: SignatureUploadRequest,
@@ -82,6 +85,7 @@ impl From<(RustUploadSigningKeysRequest, RustSignatureUploadRequest)>
}
}
#[derive(uniffi::Enum)]
pub enum OutgoingVerificationRequest {
ToDevice { request_id: String, event_type: String, body: String },
InRoom { request_id: String, room_id: String, event_type: String, content: String },
@@ -112,15 +116,15 @@ impl From<ToDeviceRequest> for OutgoingVerificationRequest {
}
}
#[derive(Debug)]
#[derive(Debug, uniffi::Enum)]
pub enum Request {
ToDevice { request_id: String, event_type: String, body: String },
KeysUpload { request_id: String, body: String },
KeysQuery { request_id: String, users: Vec<String> },
KeysClaim { request_id: String, one_time_keys: HashMap<String, HashMap<String, String>> },
KeysBackup { request_id: String, version: String, rooms: String },
RoomMessage { request_id: String, room_id: String, event_type: String, content: String },
SignatureUpload { request_id: String, body: String },
KeysBackup { request_id: String, version: String, rooms: String },
}
impl From<OutgoingRequest> for Request {
@@ -132,6 +136,7 @@ impl From<OutgoingRequest> for Request {
let body = json!({
"device_keys": u.device_keys,
"one_time_keys": u.one_time_keys,
"fallback_keys": u.fallback_keys,
});
Request::KeysUpload {
@@ -220,6 +225,7 @@ pub(crate) fn response_from_string(body: &str) -> Response<Vec<u8>> {
.expect("Can't create HTTP response")
}
#[derive(uniffi::Enum)]
pub enum RequestType {
KeysQuery,
KeysClaim,
@@ -230,6 +236,7 @@ pub enum RequestType {
RoomMessage,
}
#[derive(uniffi::Record)]
pub struct DeviceLists {
pub changed: Vec<String>,
pub left: Vec<String>,
@@ -252,6 +259,7 @@ impl From<DeviceLists> for RumaDeviceLists {
}
}
#[derive(uniffi::Record)]
pub struct KeysImportResult {
/// The number of room keys that were imported.
pub imported: i64,
@@ -4,6 +4,7 @@ use crate::CryptoStoreError;
/// Enum representing cross signing identities of our own user or some other
/// user.
#[derive(uniffi::Enum)]
pub enum UserIdentity {
/// Our own user identity.
Own {
@@ -0,0 +1,781 @@
use std::sync::Arc;
use base64::{
alphabet,
engine::{general_purpose, GeneralPurpose},
Engine,
};
use futures_util::{Stream, StreamExt};
use matrix_sdk_crypto::{
matrix_sdk_qrcode::QrVerificationData, CancelInfo as RustCancelInfo, QrVerification as InnerQr,
QrVerificationState, Sas as InnerSas, SasState as RustSasState,
Verification as InnerVerification, VerificationRequest as InnerVerificationRequest,
VerificationRequestState as RustVerificationRequestState,
};
use ruma::events::key::verification::VerificationMethod;
use tokio::runtime::Handle;
use crate::{CryptoStoreError, OutgoingVerificationRequest, SignatureUploadRequest};
const STANDARD_NO_PAD: GeneralPurpose =
GeneralPurpose::new(&alphabet::STANDARD, general_purpose::NO_PAD);
/// Listener that will be passed over the FFI to report changes to a SAS
/// verification.
pub trait SasListener: Send {
/// The callback that should be called on the Rust side
///
/// # Arguments
///
/// * `state` - The current state of the SAS verification.
fn on_change(&self, state: SasState);
}
/// An Enum describing the state the SAS verification is in.
pub enum SasState {
/// The verification has been started, the protocols that should be used
/// have been proposed and can be accepted.
Started,
/// The verification has been accepted and both sides agreed to a set of
/// protocols that will be used for the verification process.
Accepted,
/// The public keys have been exchanged and the short auth string can be
/// presented to the user.
KeysExchanged {
/// The emojis that represent the short auth string, will be `None` if
/// the emoji SAS method wasn't one of accepted protocols.
emojis: Option<Vec<i32>>,
/// The list of decimals that represent the short auth string.
decimals: Vec<i32>,
},
/// The verification process has been confirmed from our side, we're waiting
/// for the other side to confirm as well.
Confirmed,
/// The verification process has been successfully concluded.
Done,
/// The verification process has been cancelled.
Cancelled {
/// Information about the reason of the cancellation.
cancel_info: CancelInfo,
},
}
impl From<RustSasState> for SasState {
fn from(s: RustSasState) -> Self {
match s {
RustSasState::Started { .. } => Self::Started,
RustSasState::Accepted { .. } => Self::Accepted,
RustSasState::KeysExchanged { emojis, decimals } => Self::KeysExchanged {
emojis: emojis.map(|e| e.indices.map(|i| i as i32).to_vec()),
decimals: [decimals.0.into(), decimals.1.into(), decimals.2.into()].to_vec(),
},
RustSasState::Confirmed => Self::Confirmed,
RustSasState::Done { .. } => Self::Done,
RustSasState::Cancelled(c) => Self::Cancelled { cancel_info: c.into() },
}
}
}
/// Enum representing the different verification flows we support.
#[derive(uniffi::Object)]
pub struct Verification {
pub(crate) inner: InnerVerification,
pub(crate) runtime: Handle,
}
#[uniffi::export]
impl Verification {
/// Try to represent the `Verification` as an `Sas` verification object,
/// returns `None` if the verification is not a `Sas` verification.
pub fn as_sas(&self) -> Option<Arc<Sas>> {
if let InnerVerification::SasV1(sas) = &self.inner {
Some(Sas { inner: sas.to_owned(), runtime: self.runtime.to_owned() }.into())
} else {
None
}
}
/// Try to represent the `Verification` as an `QrCode` verification object,
/// returns `None` if the verification is not a `QrCode` verification.
pub fn as_qr(&self) -> Option<Arc<QrCode>> {
if let InnerVerification::QrV1(qr) = &self.inner {
Some(QrCode { inner: qr.to_owned(), runtime: self.runtime.to_owned() }.into())
} else {
None
}
}
}
/// The `m.sas.v1` verification flow.
#[derive(uniffi::Object)]
pub struct Sas {
pub(crate) inner: InnerSas,
pub(crate) runtime: Handle,
}
#[uniffi::export]
impl Sas {
/// Get the user id of the other side.
pub fn other_user_id(&self) -> String {
self.inner.other_user_id().to_string()
}
/// Get the device ID of the other side.
pub fn other_device_id(&self) -> String {
self.inner.other_device_id().to_string()
}
/// Get the unique ID that identifies this SAS verification flow.
pub fn flow_id(&self) -> String {
self.inner.flow_id().as_str().to_owned()
}
/// Get the room id if the verification is happening inside a room.
pub fn room_id(&self) -> Option<String> {
self.inner.room_id().map(|r| r.to_string())
}
/// Is the SAS flow done.
pub fn is_done(&self) -> bool {
self.inner.is_done()
}
/// Did we initiate the verification flow.
pub fn we_started(&self) -> bool {
self.inner.we_started()
}
/// Accept that we're going forward with the short auth string verification.
pub fn accept(&self) -> Option<OutgoingVerificationRequest> {
self.inner.accept().map(|r| r.into())
}
/// Confirm a verification was successful.
///
/// This method should be called if a short auth string should be confirmed
/// as matching.
pub fn confirm(&self) -> Result<Option<ConfirmVerificationResult>, CryptoStoreError> {
let (requests, signature_request) = self.runtime.block_on(self.inner.confirm())?;
let requests = requests.into_iter().map(|r| r.into()).collect();
Ok(Some(ConfirmVerificationResult {
requests,
signature_request: signature_request.map(|s| s.into()),
}))
}
/// Cancel the SAS verification using the given cancel code.
///
/// # Arguments
///
/// * `cancel_code` - The error code for why the verification was cancelled,
/// manual cancellatio usually happens with `m.user` cancel code. The full
/// list of cancel codes can be found in the [spec]
///
/// [spec]: https://spec.matrix.org/unstable/client-server-api/#mkeyverificationcancel
pub fn cancel(&self, cancel_code: String) -> Option<OutgoingVerificationRequest> {
self.inner.cancel_with_code(cancel_code.into()).map(|r| r.into())
}
/// Get a list of emoji indices of the emoji representation of the short
/// auth string.
///
/// *Note*: A SAS verification needs to be started and in the presentable
/// state for this to return the list of emoji indices, otherwise returns
/// `None`.
pub fn get_emoji_indices(&self) -> Option<Vec<i32>> {
self.inner.emoji_index().map(|v| v.iter().map(|i| (*i).into()).collect())
}
/// Get the decimal representation of the short auth string.
///
/// *Note*: A SAS verification needs to be started and in the presentable
/// state for this to return the list of decimals, otherwise returns
/// `None`.
pub fn get_decimals(&self) -> Option<Vec<i32>> {
self.inner.decimals().map(|v| [v.0.into(), v.1.into(), v.2.into()].to_vec())
}
/// Set a listener for changes in the SAS verification process.
///
/// The given callback will be called whenever the state changes.
///
/// This method can be used to react to changes in the state of the
/// verification process, or rather the method can be used to handle
/// each step of the verification process.
///
/// This method will spawn a tokio task on the Rust side, once we reach the
/// Done or Cancelled state, the task will stop listening for changes.
///
/// # Flowchart
///
/// The flow of the verification process is pictured bellow. Please note
/// that the process can be cancelled at each step of the process.
/// Either side can cancel the process.
///
/// ```text
/// ┌───────┐
/// │Started│
/// └───┬───┘
/// │
/// ┌────⌄───┐
/// │Accepted│
/// └────┬───┘
/// │
/// ┌───────⌄──────┐
/// │Keys Exchanged│
/// └───────┬──────┘
/// │
/// ________⌄________
/// ╲ ┌─────────┐
/// Does the short ╲______│Cancelled│
/// ╲ auth string match no └─────────┘
/// ╲_________________
/// │yes
/// │
/// ┌────⌄────┐
/// │Confirmed│
/// └────┬────┘
/// │
/// ┌───⌄───┐
/// │ Done │
/// └───────┘
/// ```
pub fn set_changes_listener(&self, listener: Box<dyn SasListener>) {
let stream = self.inner.changes();
self.runtime.spawn(Self::changes_listener(stream, listener));
}
/// Get the current state of the SAS verification process.
pub fn state(&self) -> SasState {
self.inner.state().into()
}
}
impl Sas {
async fn changes_listener(
mut stream: impl Stream<Item = RustSasState> + std::marker::Unpin,
listener: Box<dyn SasListener>,
) {
while let Some(state) = stream.next().await {
// If we receive a done or a cancelled state we're at the end of our road, we
// break out of the loop to deallocate the stream and finish the
// task.
let should_break =
matches!(state, RustSasState::Done { .. } | RustSasState::Cancelled { .. });
listener.on_change(state.into());
if should_break {
break;
}
}
}
}
/// Listener that will be passed over the FFI to report changes to a QrCode
/// verification.
pub trait QrCodeListener: Send {
/// The callback that should be called on the Rust side
///
/// # Arguments
///
/// * `state` - The current state of the QrCode verification.
fn on_change(&self, state: QrCodeState);
}
/// An Enum describing the state the QrCode verification is in.
pub enum QrCodeState {
/// The QR verification has been started.
Started,
/// The QR verification has been scanned by the other side.
Scanned,
/// The scanning of the QR code has been confirmed by us.
Confirmed,
/// We have successfully scanned the QR code and are able to send a
/// reciprocation event.
Reciprocated,
/// The verification process has been successfully concluded.
Done,
/// The verification process has been cancelled.
Cancelled {
/// Information about the reason of the cancellation.
cancel_info: CancelInfo,
},
}
impl From<QrVerificationState> for QrCodeState {
fn from(value: QrVerificationState) -> Self {
match value {
QrVerificationState::Started => Self::Started,
QrVerificationState::Scanned => Self::Scanned,
QrVerificationState::Confirmed => Self::Confirmed,
QrVerificationState::Reciprocated => Self::Reciprocated,
QrVerificationState::Done { .. } => Self::Done,
QrVerificationState::Cancelled(c) => Self::Cancelled { cancel_info: c.into() },
}
}
}
/// The `m.qr_code.scan.v1`, `m.qr_code.show.v1`, and `m.reciprocate.v1`
/// verification flow.
#[derive(uniffi::Object)]
pub struct QrCode {
pub(crate) inner: InnerQr,
pub(crate) runtime: Handle,
}
#[uniffi::export]
impl QrCode {
/// Get the user id of the other side.
pub fn other_user_id(&self) -> String {
self.inner.other_user_id().to_string()
}
/// Get the device ID of the other side.
pub fn other_device_id(&self) -> String {
self.inner.other_device_id().to_string()
}
/// Get the unique ID that identifies this QR code verification flow.
pub fn flow_id(&self) -> String {
self.inner.flow_id().as_str().to_owned()
}
/// Get the room id if the verification is happening inside a room.
pub fn room_id(&self) -> Option<String> {
self.inner.room_id().map(|r| r.to_string())
}
/// Is the QR code verification done.
pub fn is_done(&self) -> bool {
self.inner.is_done()
}
/// Has the verification flow been cancelled.
pub fn is_cancelled(&self) -> bool {
self.inner.is_cancelled()
}
/// Did we initiate the verification flow.
pub fn we_started(&self) -> bool {
self.inner.we_started()
}
/// Get the CancelInfo of this QR code verification object.
///
/// Will be `None` if the flow has not been cancelled.
pub fn cancel_info(&self) -> Option<CancelInfo> {
self.inner.cancel_info().map(|c| c.into())
}
/// Has the QR verification been scanned by the other side.
///
/// When the verification object is in this state it's required that the
/// user confirms that the other side has scanned the QR code.
pub fn has_been_scanned(&self) -> bool {
self.inner.has_been_scanned()
}
/// Have we successfully scanned the QR code and are able to send a
/// reciprocation event.
pub fn reciprocated(&self) -> bool {
self.inner.reciprocated()
}
/// Cancel the QR code verification using the given cancel code.
///
/// # Arguments
///
/// * `cancel_code` - The error code for why the verification was cancelled,
/// manual cancellatio usually happens with `m.user` cancel code. The full
/// list of cancel codes can be found in the [spec]
///
/// [spec]: https://spec.matrix.org/unstable/client-server-api/#mkeyverificationcancel
pub fn cancel(&self, cancel_code: String) -> Option<OutgoingVerificationRequest> {
self.inner.cancel_with_code(cancel_code.into()).map(|r| r.into())
}
/// Confirm a verification was successful.
///
/// This method should be called if we want to confirm that the other side
/// has scanned our QR code.
pub fn confirm(&self) -> Option<ConfirmVerificationResult> {
self.inner.confirm_scanning().map(|r| ConfirmVerificationResult {
requests: vec![r.into()],
signature_request: None,
})
}
/// Generate data that should be encoded as a QR code.
///
/// This method should be called right before a QR code should be displayed,
/// the returned data is base64 encoded (without padding) and needs to be
/// decoded on the other side before it can be put through a QR code
/// generator.
pub fn generate_qr_code(&self) -> Option<String> {
self.inner.to_bytes().map(|data| STANDARD_NO_PAD.encode(data)).ok()
}
/// Set a listener for changes in the QrCode verification process.
///
/// The given callback will be called whenever the state changes.
pub fn set_changes_listener(&self, listener: Box<dyn QrCodeListener>) {
let stream = self.inner.changes();
self.runtime.spawn(Self::changes_listener(stream, listener));
}
/// Get the current state of the QrCode verification process.
pub fn state(&self) -> QrCodeState {
self.inner.state().into()
}
}
impl QrCode {
async fn changes_listener(
mut stream: impl Stream<Item = QrVerificationState> + std::marker::Unpin,
listener: Box<dyn QrCodeListener>,
) {
while let Some(state) = stream.next().await {
// If we receive a done or a cancelled state we're at the end of our road, we
// break out of the loop to deallocate the stream and finish the
// task.
let should_break = matches!(
state,
QrVerificationState::Done { .. } | QrVerificationState::Cancelled { .. }
);
listener.on_change(state.into());
if should_break {
break;
}
}
}
}
/// Information on why a verification flow has been cancelled and by whom.
pub struct CancelInfo {
/// The textual representation of the cancel reason
pub reason: String,
/// The code describing the cancel reason
pub cancel_code: String,
/// Was the verification flow cancelled by us
pub cancelled_by_us: bool,
}
impl From<RustCancelInfo> for CancelInfo {
fn from(c: RustCancelInfo) -> Self {
Self {
reason: c.reason().to_owned(),
cancel_code: c.cancel_code().to_string(),
cancelled_by_us: c.cancelled_by_us(),
}
}
}
/// A result type for starting SAS verifications.
#[derive(uniffi::Record)]
pub struct StartSasResult {
/// The SAS verification object that got created.
pub sas: Arc<Sas>,
/// The request that needs to be sent out to notify the other side that a
/// SAS verification should start.
pub request: OutgoingVerificationRequest,
}
/// A result type for scanning QR codes.
#[derive(uniffi::Record)]
pub struct ScanResult {
/// The QR code verification object that got created.
pub qr: Arc<QrCode>,
/// The request that needs to be sent out to notify the other side that a
/// QR code verification should start.
pub request: OutgoingVerificationRequest,
}
/// A result type for requesting verifications.
#[derive(uniffi::Record)]
pub struct RequestVerificationResult {
/// The verification request object that got created.
pub verification: Arc<VerificationRequest>,
/// The request that needs to be sent out to notify the other side that
/// we're requesting verification to begin.
pub request: OutgoingVerificationRequest,
}
/// A result type for confirming verifications.
#[derive(uniffi::Record)]
pub struct ConfirmVerificationResult {
/// The requests that needs to be sent out to notify the other side that we
/// confirmed the verification.
pub requests: Vec<OutgoingVerificationRequest>,
/// A request that will upload signatures of the verified device or user, if
/// the verification is completed and we're able to sign devices or users
pub signature_request: Option<SignatureUploadRequest>,
}
/// Listener that will be passed over the FFI to report changes to a
/// verification request.
pub trait VerificationRequestListener: Send {
/// The callback that should be called on the Rust side
///
/// # Arguments
///
/// * `state` - The current state of the verification request.
fn on_change(&self, state: VerificationRequestState);
}
/// An Enum describing the state the QrCode verification is in.
pub enum VerificationRequestState {
/// The verification request was sent
Requested,
/// The verification request is ready to start a verification flow.
Ready {
/// The verification methods supported by the other side.
their_methods: Vec<String>,
/// The verification methods supported by the us.
our_methods: Vec<String>,
},
/// The verification flow that was started with this request has finished.
Done,
/// The verification process has been cancelled.
Cancelled {
/// Information about the reason of the cancellation.
cancel_info: CancelInfo,
},
}
impl From<RustVerificationRequestState> for VerificationRequestState {
fn from(value: RustVerificationRequestState) -> Self {
match value {
// The clients do not need to distinguish `Created` and `Requested` state
RustVerificationRequestState::Created { .. } => Self::Requested,
RustVerificationRequestState::Requested { .. } => Self::Requested,
RustVerificationRequestState::Ready {
their_methods,
our_methods,
other_device_id: _,
} => Self::Ready {
their_methods: their_methods.iter().map(|m| m.to_string()).collect(),
our_methods: our_methods.iter().map(|m| m.to_string()).collect(),
},
RustVerificationRequestState::Done => Self::Done,
RustVerificationRequestState::Cancelled(c) => Self::Cancelled { cancel_info: c.into() },
}
}
}
/// The verificatoin request object which then can transition into some concrete
/// verification method
#[derive(uniffi::Object)]
pub struct VerificationRequest {
pub(crate) inner: InnerVerificationRequest,
pub(crate) runtime: Handle,
}
#[uniffi::export]
impl VerificationRequest {
/// The id of the other user that is participating in this verification
/// request.
pub fn other_user_id(&self) -> String {
self.inner.other_user().to_string()
}
/// The id of the other device that is participating in this verification.
pub fn other_device_id(&self) -> Option<String> {
self.inner.other_device_id().map(|d| d.to_string())
}
/// Get the unique ID of this verification request
pub fn flow_id(&self) -> String {
self.inner.flow_id().as_str().to_owned()
}
/// Get the room id if the verification is happening inside a room.
pub fn room_id(&self) -> Option<String> {
self.inner.room_id().map(|r| r.to_string())
}
/// Has the verification flow that was started with this request finished.
pub fn is_done(&self) -> bool {
self.inner.is_done()
}
/// Is the verification request ready to start a verification flow.
pub fn is_ready(&self) -> bool {
self.inner.is_ready()
}
/// Did we initiate the verification request
pub fn we_started(&self) -> bool {
self.inner.we_started()
}
/// Has the verification request been answered by another device.
pub fn is_passive(&self) -> bool {
self.inner.is_passive()
}
/// Has the verification flow that been cancelled.
pub fn is_cancelled(&self) -> bool {
self.inner.is_cancelled()
}
/// Get info about the cancellation if the verification request has been
/// cancelled.
pub fn cancel_info(&self) -> Option<CancelInfo> {
self.inner.cancel_info().map(|v| v.into())
}
/// Get the supported verification methods of the other side.
///
/// Will be present only if the other side requested the verification or if
/// we're in the ready state.
pub fn their_supported_methods(&self) -> Option<Vec<String>> {
self.inner.their_supported_methods().map(|m| m.iter().map(|m| m.to_string()).collect())
}
/// Get our own supported verification methods that we advertised.
///
/// Will be present only we requested the verification or if we're in the
/// ready state.
pub fn our_supported_methods(&self) -> Option<Vec<String>> {
self.inner.our_supported_methods().map(|m| m.iter().map(|m| m.to_string()).collect())
}
/// Accept a verification requests that we share with the given user with
/// the given flow id.
///
/// This will move the verification request into the ready state.
///
/// # Arguments
///
/// * `user_id` - The ID of the user for which we would like to accept the
/// verification requests.
///
/// * `flow_id` - The ID that uniquely identifies the verification flow.
///
/// * `methods` - A list of verification methods that we want to advertise
/// as supported.
pub fn accept(&self, methods: Vec<String>) -> Option<OutgoingVerificationRequest> {
let methods = methods.into_iter().map(VerificationMethod::from).collect();
self.inner.accept_with_methods(methods).map(|r| r.into())
}
/// Cancel a verification for the given user with the given flow id using
/// the given cancel code.
pub fn cancel(&self) -> Option<OutgoingVerificationRequest> {
self.inner.cancel().map(|r| r.into())
}
/// Transition from a verification request into short auth string based
/// verification.
///
/// # Arguments
///
/// * `user_id` - The ID of the user for which we would like to start the
/// SAS verification.
///
/// * `flow_id` - The ID of the verification request that initiated the
/// verification flow.
pub fn start_sas_verification(&self) -> Result<Option<StartSasResult>, CryptoStoreError> {
Ok(self.runtime.block_on(self.inner.start_sas())?.map(|(sas, r)| StartSasResult {
sas: Arc::new(Sas { inner: sas, runtime: self.runtime.clone() }),
request: r.into(),
}))
}
/// Transition from a verification request into QR code verification.
///
/// This method should be called when one wants to display a QR code so the
/// other side can scan it and move the QR code verification forward.
///
/// # Arguments
///
/// * `user_id` - The ID of the user for which we would like to start the
/// QR code verification.
///
/// * `flow_id` - The ID of the verification request that initiated the
/// verification flow.
pub fn start_qr_verification(&self) -> Result<Option<Arc<QrCode>>, CryptoStoreError> {
Ok(self
.runtime
.block_on(self.inner.generate_qr_code())?
.map(|qr| QrCode { inner: qr, runtime: self.runtime.clone() }.into()))
}
/// Pass data from a scanned QR code to an active verification request and
/// transition into QR code verification.
///
/// This requires an active `VerificationRequest` to succeed, returns `None`
/// if no `VerificationRequest` is found or if the QR code data is invalid.
///
/// # Arguments
///
/// * `user_id` - The ID of the user for which we would like to start the
/// QR code verification.
///
/// * `flow_id` - The ID of the verification request that initiated the
/// verification flow.
///
/// * `data` - The data that was extracted from the scanned QR code as an
/// base64 encoded string, without padding.
pub fn scan_qr_code(&self, data: String) -> Option<ScanResult> {
let data = STANDARD_NO_PAD.decode(data).ok()?;
let data = QrVerificationData::from_bytes(data).ok()?;
if let Some(qr) = self.runtime.block_on(self.inner.scan_qr_code(data)).ok()? {
let request = qr.reciprocate()?;
Some(ScanResult {
qr: QrCode { inner: qr, runtime: self.runtime.clone() }.into(),
request: request.into(),
})
} else {
None
}
}
/// Set a listener for changes in the verification request
///
/// The given callback will be called whenever the state changes.
pub fn set_changes_listener(&self, listener: Box<dyn VerificationRequestListener>) {
let stream = self.inner.changes();
self.runtime.spawn(Self::changes_listener(stream, listener));
}
/// Get the current state of the verification request.
pub fn state(&self) -> VerificationRequestState {
self.inner.state().into()
}
}
impl VerificationRequest {
async fn changes_listener(
mut stream: impl Stream<Item = RustVerificationRequestState> + std::marker::Unpin,
listener: Box<dyn VerificationRequestListener>,
) {
while let Some(state) = stream.next().await {
// If we receive a done or a cancelled state we're at the end of our road, we
// break out of the loop to deallocate the stream and finish the
// task.
let should_break = matches!(
state,
RustVerificationRequestState::Done { .. }
| RustVerificationRequestState::Cancelled { .. }
);
listener.on_change(state.into());
if should_break {
break;
}
}
}
}
@@ -0,0 +1,6 @@
[bindings.kotlin]
package_name = "org.matrix.rustcomponents.sdk.crypto"
cdylib_name = "matrix_sdk_crypto_ffi"
[bindings.swift]
module_name = "MatrixSDKCrypto"
@@ -0,0 +1,2 @@
[build]
target = "wasm32-unknown-unknown"
+3
View File
@@ -0,0 +1,3 @@
/docs
/node_modules
/package-lock.json
@@ -0,0 +1 @@
/pkg
@@ -0,0 +1,9 @@
// prettier configuration: the same as the conventions used throughout Matrix.org
// see: https://github.com/matrix-org/eslint-plugin-matrix-org/blob/main/.prettierrc.js
module.exports = {
printWidth: 120,
tabWidth: 4,
quoteProps: "consistent",
trailingComma: "all",
};
+2
View File
@@ -0,0 +1,2 @@
version-tag-prefix "matrix-sdk-crypto-js-"
version-git-message "matrix-sdk-crypto-js v%s"
@@ -0,0 +1,11 @@
# v0.1.0-alpha.7
- Add new accessors `Device.algorithms` and `Device.isSignedByOwner`
- In `OlmMachine.getUserDevices`, wait a limited time for any in-flight
device-list updates to complete.
# v0.1.0-alpha.6
- Add new accessor `InboundGroupSession.senderKey`.
- Add a new API, `OlmMachine.registerRoomKeyUpdatedCallback`, which
applications can use to listen for received room keys.
+54
View File
@@ -0,0 +1,54 @@
[package]
name = "matrix-sdk-crypto-js"
description = "Matrix encryption library, for JavaScript"
authors = ["Ivan Enderlin <ivane@element.io>"]
edition = "2021"
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
keywords = ["matrix", "chat", "messaging", "ruma", "nio"]
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/matrix-org/matrix-rust-sdk"
rust-version = { workspace = true }
version = "0.1.0-alpha.0"
publish = false
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.wasm-pack.profile.profiling]
wasm-opt = false
[package.metadata.wasm-pack.profile.profiling.wasm-bindgen]
debug-js-glue = false
demangle-name-section = true
dwarf-debug-info = true
[package.metadata.wasm-pack.profile.release]
wasm-opt = ['-Oz']
[lib]
crate-type = ["cdylib"]
[features]
default = ["tracing", "qrcode"]
qrcode = ["matrix-sdk-crypto/qrcode", "dep:matrix-sdk-qrcode"]
tracing = []
[dependencies]
anyhow = { workspace = true }
console_error_panic_hook = "0.1.7"
futures-util = "0.3.27"
http = { workspace = true }
js-sys = "0.3.49"
matrix-sdk-common = { version = "0.6.0", path = "../../crates/matrix-sdk-common", features = ["js"] }
matrix-sdk-crypto = { version = "0.6.0", path = "../../crates/matrix-sdk-crypto", features = ["js"] }
matrix-sdk-indexeddb = { version = "0.2.0", path = "../../crates/matrix-sdk-indexeddb" }
matrix-sdk-qrcode = { version = "0.4.0", path = "../../crates/matrix-sdk-qrcode", optional = true }
ruma = { workspace = true, features = ["js", "rand", "unstable-msc2677"] }
serde_json = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { version = "0.3.14", default-features = false, features = ["registry", "std"] }
vodozemac = { workspace = true, features = ["js"] }
wasm-bindgen = "0.2.83"
wasm-bindgen-futures = "0.4.33"
zeroize = { workspace = true }
+57
View File
@@ -0,0 +1,57 @@
# `matrix-sdk-crypto-js`
Welcome to the [WebAssembly] + JavaScript binding for the Rust
[`matrix-sdk-crypto`] library! WebAssembly can run anywhere, but these
bindings are designed to run on a JavaScript host. These bindings are
part of the [`matrix-rust-sdk`] project, which is a library
implementation of a [Matrix] client-server.
`matrix-sdk-crypto` is a no-network-IO implementation of a state
machine, named `OlmMachine`, that handles E2EE ([End-to-End
Encryption](https://en.wikipedia.org/wiki/End-to-end_encryption)) for
[Matrix] clients.
## Usage
These WebAssembly bindings are written in [Rust]. To build them, you
need to install the Rust compiler, see [the Install Rust
Page](https://www.rust-lang.org/tools/install). Then, the workflow is
pretty classical by using [npm], see [the Downloading and installing
Node.js and npm
Page](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).
Once the Rust compiler, Node.js and npm are installed, you can run the
following commands:
```sh
$ npm install
$ npm run build
$ npm run test
```
A `matrix_sdk_crypto.js`, `matrix_sdk_crypto.d.ts` and a
`matrix_sdk_crypto_bg.wasm` files should be generated in the `pkg/`
directory.
TBD
## Documentation
[The documentation can be found
online](https://matrix-org.github.io/matrix-rust-sdk/bindings/matrix-sdk-crypto-js/).
To generate the documentation locally, please run the following
command:
```sh
$ npm run doc
```
The documentation is generated in the `./docs` directory.
[WebAssembly]: https://webassembly.org/
[`matrix-sdk-crypto`]: https://github.com/matrix-org/matrix-rust-sdk/tree/main/crates/matrix-sdk-crypto
[`matrix-rust-sdk`]: https://github.com/matrix-org/matrix-rust-sdk
[Matrix]: https://matrix.org/
[Rust]: https://www.rust-lang.org/
[npm]: https://www.npmjs.com/
@@ -0,0 +1,13 @@
# Steps for releasing `matrix-sdk-crypto-js`
1. Create a new branch, named `release-matrix-sdk-crypto-js-<version>`.
2. Update `CHANGELOG.md`, if necessary.
3. Run `yarn version` to bump the version number and create a tag.
4. Push the branch, but not yet the tag.
5. Create a PR to approve the changes.
6. Once approved:
1. Update the git tag to the new head of the branch, if necessary.
2. Push the git tag; doing so triggers the github actions workflow which
builds and publishes to npm, and creates a draft GH release.
3. Merge the PR.
7. Update the release on github and publish.
@@ -0,0 +1,49 @@
{
"name": "@matrix-org/matrix-sdk-crypto-js",
"version": "0.1.0-alpha.7",
"homepage": "https://github.com/matrix-org/matrix-rust-sdk",
"description": "Matrix encryption library, for JavaScript",
"license": "Apache-2.0",
"collaborators": [
"Ivan Enderlin <ivane@element.io>"
],
"repository": {
"type": "git",
"url": "https://github.com/matrix-org/matrix-rust-sdk"
},
"keywords": [
"matrix",
"chat",
"messaging",
"ruma",
"nio"
],
"main": "pkg/matrix_sdk_crypto_js.js",
"types": "pkg/matrix_sdk_crypto_js.d.ts",
"files": [
"pkg/matrix_sdk_crypto_js_bg.wasm.js",
"pkg/matrix_sdk_crypto_js_bg.wasm.d.ts",
"pkg/matrix_sdk_crypto_js.js",
"pkg/matrix_sdk_crypto_js.d.ts"
],
"devDependencies": {
"cross-env": "^7.0.3",
"fake-indexeddb": "^4.0",
"jest": "^28.1.0",
"prettier": "^2.8.3",
"typedoc": "^0.22.17",
"wasm-pack": "^0.10.2",
"yargs-parser": "~21.0.1"
},
"engines": {
"node": ">= 10"
},
"scripts": {
"lint": "prettier --check .",
"build": "WASM_PACK_ARGS=--release ./scripts/build.sh",
"build:dev": "WASM_PACK_ARGS=--dev ./scripts/build.sh",
"test": "jest --verbose",
"doc": "typedoc --tsconfig .",
"prepack": "npm run build && npm run test"
}
}
+35
View File
@@ -0,0 +1,35 @@
#!/bin/bash
#
# Build the JavaScript modules
#
# This script is really a workaround for https://github.com/rustwasm/wasm-pack/issues/1074.
#
# Currently, the only reliable way to load WebAssembly in all the JS
# environments we want to target (web-via-webpack, web-via-browserify, jest)
# seems to be to pack the WASM into base64, and then unpack it and instantiate
# it at runtime.
#
# Hopefully one day, https://github.com/rustwasm/wasm-pack/issues/1074 will be
# fixed and this will be unnecessary.
set -e
cd $(dirname "$0")/..
RUSTFLAGS='-C opt-level=z' WASM_BINDGEN_WEAKREF=1 wasm-pack build --target nodejs --scope matrix-org --out-dir pkg "${WASM_PACK_ARGS[@]}"
# Convert the Wasm into a JS file that exports the base64'ed Wasm.
echo "module.exports = \`$(base64 pkg/matrix_sdk_crypto_js_bg.wasm)\`;" > pkg/matrix_sdk_crypto_js_bg.wasm.js
# In the JavaScript:
# 1. Strip out the lines that load the WASM, and our new epilogue.
# 2. Remove the imports of `TextDecoder` and `TextEncoder`. We rely on the global defaults.
{
sed -e '/Text..coder.*= require(.util.)/d' \
-e '/^const path = /,$d' pkg/matrix_sdk_crypto_js.js
cat scripts/epilogue.js
} > pkg/matrix_sdk_crypto_js.js.new
mv pkg/matrix_sdk_crypto_js.js.new pkg/matrix_sdk_crypto_js.js
# also extend the typescript
cat scripts/epilogue.d.ts >> pkg/matrix_sdk_crypto_js.d.ts
+8
View File
@@ -0,0 +1,8 @@
/**
* Load the WebAssembly module in the background, if it has not already been loaded.
*
* Returns a promise which will resolve once the other methods are ready.
*
* @returns {Promise<void>}
*/
export function initAsync(): Promise<void>;
@@ -0,0 +1,88 @@
// Initially, 'wasm' is set to a Proxy object which will synchronously load the WebAssembly module and
// replace 'wasm' with a reference to the exports from the wasm module.
//
// Ideally this will never get used because the application will call initAsync instead.
wasm = new Proxy(
{},
{
get: (target, prop, receiver) => __initSync()[prop],
},
);
let inited = false;
__initSync = function () {
if (inited) {
return;
}
if (initPromise) {
throw new Error("Asynchronous initialisation already in progress: cannot initialise synchronously");
}
const bytes = unbase64(require("./matrix_sdk_crypto_js_bg.wasm.js"));
const mod = new WebAssembly.Module(bytes);
const instance = new WebAssembly.Instance(mod, imports);
wasm = instance.exports;
wasm.__wbindgen_start();
inited = true;
return wasm;
};
let initPromise = null;
/**
* Load the WebAssembly module in the background, if it has not already been loaded.
*
* Returns a promise which will resolve once the other methods are ready.
*
* @returns {Promise<void>}
*/
module.exports.initAsync = function () {
if (inited) {
return Promise.resolve();
}
if (!initPromise) {
initPromise = Promise.resolve()
.then(() => require("./matrix_sdk_crypto_js_bg.wasm.js"))
.then((b64) => WebAssembly.instantiate(unbase64(b64), imports))
.then((result) => {
wasm = result.instance.exports;
wasm.__wbindgen_start();
inited = true;
});
}
return initPromise;
};
const b64lookup = new Uint8Array([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 62, 0, 62, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
]);
// base64 decoder, based on the code at https://developer.mozilla.org/en-US/docs/Glossary/Base64#solution_2_%E2%80%93_rewriting_atob_and_btoa_using_typedarrays_and_utf-8
function unbase64(sBase64) {
const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, "");
const nInLen = sB64Enc.length;
const nOutLen = (nInLen * 3 + 1) >> 2;
const taBytes = new Uint8Array(nOutLen);
let nMod3;
let nMod4;
let nUint24 = 0;
let nOutIdx = 0;
for (let nInIdx = 0; nInIdx < nInLen; nInIdx++) {
nMod4 = nInIdx & 3;
nUint24 |= b64lookup[sB64Enc.charCodeAt(nInIdx)] << (6 * (3 - nMod4));
if (nMod4 === 3 || nInLen - nInIdx === 1) {
nMod3 = 0;
while (nMod3 < 3 && nOutIdx < nOutLen) {
taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255;
nMod3++;
nOutIdx++;
}
nUint24 = 0;
}
}
return taBytes;
}
@@ -0,0 +1,121 @@
//! Attachment API.
use std::io::{Cursor, Read};
use wasm_bindgen::prelude::*;
/// A type to encrypt and to decrypt anything that can fit in an
/// `Uint8Array`, usually big buffer.
#[wasm_bindgen]
#[derive(Debug)]
pub struct Attachment;
#[wasm_bindgen]
impl Attachment {
/// Encrypt the content of the `Uint8Array`.
///
/// It produces an `EncryptedAttachment`, which can be used to
/// retrieve the media encryption information, or the encrypted
/// data.
#[wasm_bindgen]
pub fn encrypt(array: &[u8]) -> Result<EncryptedAttachment, JsError> {
let mut cursor = Cursor::new(array);
let mut encryptor = matrix_sdk_crypto::AttachmentEncryptor::new(&mut cursor);
let mut encrypted_data = Vec::new();
encryptor.read_to_end(&mut encrypted_data)?;
let media_encryption_info = Some(encryptor.finish());
Ok(EncryptedAttachment { encrypted_data, media_encryption_info })
}
/// Decrypt an `EncryptedAttachment`.
///
/// The encrypted attachment can be created manually, or from the
/// `encrypt` method.
///
/// **Warning**: The encrypted attachment can be used only
/// **once**! The encrypted data will still be present, but the
/// media encryption info (which contain secrets) will be
/// destroyed. It is still possible to get a JSON-encoded backup
/// by calling `EncryptedAttachment.mediaEncryptionInfo`.
pub fn decrypt(attachment: &mut EncryptedAttachment) -> Result<Vec<u8>, JsError> {
let Some(media_encryption_info) = attachment.media_encryption_info.take() else {
return Err(JsError::new(
"The media encryption info are absent from the given encrypted attachment",
));
};
let encrypted_data: &[u8] = attachment.encrypted_data.as_slice();
let mut cursor = Cursor::new(encrypted_data);
let mut decryptor =
matrix_sdk_crypto::AttachmentDecryptor::new(&mut cursor, media_encryption_info)?;
let mut decrypted_data = Vec::new();
decryptor.read_to_end(&mut decrypted_data)?;
Ok(decrypted_data)
}
}
/// An encrypted attachment, usually created from `Attachment.encrypt`.
#[wasm_bindgen]
#[derive(Debug)]
pub struct EncryptedAttachment {
media_encryption_info: Option<matrix_sdk_crypto::MediaEncryptionInfo>,
encrypted_data: Vec<u8>,
}
#[wasm_bindgen]
impl EncryptedAttachment {
/// Create a new encrypted attachment manually.
///
/// It needs encrypted data, stored in an `Uint8Array`, and a
/// [media encryption
/// information](https://docs.rs/matrix-sdk-crypto/latest/matrix_sdk_crypto/struct.MediaEncryptionInfo.html),
/// as a JSON-encoded string.
///
/// The media encryption information aren't stored as a string:
/// they are parsed, validated and fully deserialized.
///
/// See [the specification to learn
/// more](https://spec.matrix.org/unstable/client-server-api/#extensions-to-mroommessage-msgtypes).
#[wasm_bindgen(constructor)]
pub fn new(
encrypted_data: Vec<u8>,
media_encryption_info: &str,
) -> Result<EncryptedAttachment, JsError> {
Ok(Self {
encrypted_data,
media_encryption_info: Some(serde_json::from_str(media_encryption_info)?),
})
}
/// The actual encrypted data.
///
/// **Warning**: It returns a **copy** of the entire encrypted
/// data; be nice with your memory.
#[wasm_bindgen(getter, js_name = "encryptedData")]
pub fn encrypted_data(&self) -> Vec<u8> {
self.encrypted_data.clone()
}
/// Return the media encryption info as a JSON-encoded string. The
/// structure is fully valid.
///
/// If the media encryption info have been consumed already, it
/// will return `null`.
#[wasm_bindgen(getter, js_name = "mediaEncryptionInfo")]
pub fn media_encryption_info(&self) -> Option<String> {
serde_json::to_string(self.media_encryption_info.as_ref()?).ok()
}
/// Check whether the media encryption info has been consumed by
/// `Attachment.decrypt` already.
#[wasm_bindgen(getter, js_name = "hasMediaEncryptionInfoBeenConsumed")]
pub fn has_media_encryption_info_been_consumed(&self) -> bool {
self.media_encryption_info.is_none()
}
}
+269
View File
@@ -0,0 +1,269 @@
//! Types for a `Device`.
use js_sys::{Array, Map, Promise};
use wasm_bindgen::prelude::*;
use crate::{
encryption::EncryptionAlgorithm,
future::future_to_promise,
identifiers::{self, DeviceId, UserId},
impl_from_to_inner,
js::try_array_to_vec,
types, verification, vodozemac,
};
/// A device represents a E2EE capable client of an user.
#[wasm_bindgen]
#[derive(Debug)]
pub struct Device {
pub(crate) inner: matrix_sdk_crypto::Device,
}
impl_from_to_inner!(matrix_sdk_crypto::Device => Device);
#[wasm_bindgen]
impl Device {
/// Request an interactive verification with this device.
#[wasm_bindgen(js_name = "requestVerification")]
pub fn request_verification(&self, methods: Option<Array>) -> Result<Promise, JsError> {
let methods =
methods.map(try_array_to_vec::<verification::VerificationMethod, _>).transpose()?;
let me = self.inner.clone();
Ok(future_to_promise(async move {
let tuple = Array::new();
let (verification_request, outgoing_verification_request) = match methods {
Some(methods) => me.request_verification_with_methods(methods).await,
None => me.request_verification().await,
};
tuple.set(0, verification::VerificationRequest::from(verification_request).into());
tuple.set(
1,
verification::OutgoingVerificationRequest::from(outgoing_verification_request)
.try_into()?,
);
Ok(tuple)
}))
}
/// Is this device considered to be verified.
///
/// This method returns true if either the `is_locally_trusted`
/// method returns `true` or if the `is_cross_signing_trusted`
/// method returns `true`.
#[wasm_bindgen(js_name = "isVerified")]
pub fn is_verified(&self) -> bool {
self.inner.is_verified()
}
/// Is this device considered to be verified using cross signing.
#[wasm_bindgen(js_name = "isCrossSigningTrusted")]
pub fn is_cross_signing_trusted(&self) -> bool {
self.inner.is_cross_signing_trusted()
}
/// Is this device cross-signed by its owner?
#[wasm_bindgen(js_name = "isCrossSignedByOwner")]
pub fn is_cross_signed_by_owner(&self) -> bool {
self.inner.is_cross_signed_by_owner()
}
/// Set the local trust state of the device to the given state.
///
/// This wont affect any cross signing trust state, this only
/// sets a flag marking to have the given trust state.
///
/// `trust_state` represents the new trust state that should be
/// set for the device.
#[wasm_bindgen(js_name = "setLocalTrust")]
pub fn set_local_trust(&self, local_state: LocalTrust) -> Promise {
let me = self.inner.clone();
future_to_promise(async move {
me.set_local_trust(local_state.into()).await?;
Ok(JsValue::NULL)
})
}
/// The user ID of the device owner.
#[wasm_bindgen(getter, js_name = "userId")]
pub fn user_id(&self) -> UserId {
self.inner.user_id().to_owned().into()
}
/// The unique ID of the device.
#[wasm_bindgen(getter, js_name = "deviceId")]
pub fn device_id(&self) -> DeviceId {
self.inner.device_id().to_owned().into()
}
/// Get the human readable name of the device.
#[wasm_bindgen(getter, js_name = "displayName")]
pub fn display_name(&self) -> Option<String> {
self.inner.display_name().map(ToOwned::to_owned)
}
/// Get the key of the given key algorithm belonging to this device.
#[wasm_bindgen(js_name = "getKey")]
pub fn get_key(
&self,
algorithm: identifiers::DeviceKeyAlgorithmName,
) -> Result<Option<vodozemac::DeviceKey>, JsError> {
Ok(self.inner.get_key(algorithm.try_into()?).cloned().map(Into::into))
}
/// Get the Curve25519 key of the given device.
#[wasm_bindgen(getter, js_name = "curve25519Key")]
pub fn curve25519_key(&self) -> Option<vodozemac::Curve25519PublicKey> {
self.inner.curve25519_key().map(Into::into)
}
/// Get the Ed25519 key of the given device.
#[wasm_bindgen(getter, js_name = "ed25519Key")]
pub fn ed25519_key(&self) -> Option<vodozemac::Ed25519PublicKey> {
self.inner.ed25519_key().map(Into::into)
}
/// Get a map containing all the device keys.
#[wasm_bindgen(getter)]
pub fn keys(&self) -> Map {
let map = Map::new();
for (device_key_id, device_key) in self.inner.keys() {
map.set(
&identifiers::DeviceKeyId::from(device_key_id.clone()).into(),
&vodozemac::DeviceKey::from(device_key.clone()).into(),
);
}
map
}
/// Get the list of algorithms this device supports.
///
/// Returns `Array<EncryptionAlgorithm>`.
#[wasm_bindgen(getter)]
pub fn algorithms(&self) -> Array {
self.inner
.algorithms()
.iter()
.map(|alg| EncryptionAlgorithm::from(alg.clone()))
.map(JsValue::from)
.collect()
}
/// Get a map containing all the device signatures.
#[wasm_bindgen(getter)]
pub fn signatures(&self) -> types::Signatures {
self.inner.signatures().clone().into()
}
/// Get the trust state of the device.
#[wasm_bindgen(getter, js_name = "localTrustState")]
pub fn local_trust_state(&self) -> LocalTrust {
self.inner.local_trust_state().into()
}
/// Is the device locally marked as trusted?
#[wasm_bindgen(js_name = "isLocallyTrusted")]
pub fn is_locally_trusted(&self) -> bool {
self.inner.is_locally_trusted()
}
/// Is the device locally marked as blacklisted?
///
/// Blacklisted devices wont receive any group sessions.
#[wasm_bindgen(js_name = "isBlacklisted")]
pub fn is_blacklisted(&self) -> bool {
self.inner.is_blacklisted()
}
/// Is the device deleted?
#[wasm_bindgen(js_name = "isDeleted")]
pub fn is_deleted(&self) -> bool {
self.inner.is_deleted()
}
}
/// The local trust state of a device.
#[wasm_bindgen]
#[derive(Debug)]
pub enum LocalTrust {
/// The device has been verified and is trusted.
Verified,
/// The device been blacklisted from communicating.
BlackListed,
/// The trust state of the device is being ignored.
Ignored,
/// The trust state is unset.
Unset,
}
impl From<matrix_sdk_crypto::LocalTrust> for LocalTrust {
fn from(value: matrix_sdk_crypto::LocalTrust) -> Self {
use matrix_sdk_crypto::LocalTrust::*;
match value {
Verified => Self::Verified,
BlackListed => Self::BlackListed,
Ignored => Self::Ignored,
Unset => Self::Unset,
}
}
}
impl From<LocalTrust> for matrix_sdk_crypto::LocalTrust {
fn from(value: LocalTrust) -> Self {
use LocalTrust::*;
match value {
Verified => Self::Verified,
BlackListed => Self::BlackListed,
Ignored => Self::Ignored,
Unset => Self::Unset,
}
}
}
/// A read only view over all devices belonging to a user.
#[wasm_bindgen]
#[derive(Debug)]
pub struct UserDevices {
pub(crate) inner: matrix_sdk_crypto::UserDevices,
}
impl_from_to_inner!(matrix_sdk_crypto::UserDevices => UserDevices);
#[wasm_bindgen]
impl UserDevices {
/// Get the specific device with the given device ID.
pub fn get(&self, device_id: &DeviceId) -> Option<Device> {
self.inner.get(&device_id.inner).map(Into::into)
}
/// Returns true if there is at least one devices of this user
/// that is considered to be verified, false otherwise.
///
/// This won't consider your own device as verified, as your own
/// device is always implicitly verified.
#[wasm_bindgen(js_name = "isAnyVerified")]
pub fn is_any_verified(&self) -> bool {
self.inner.is_any_verified()
}
/// Array over all the device IDs of the user devices.
pub fn keys(&self) -> Array {
self.inner.keys().map(ToOwned::to_owned).map(DeviceId::from).map(JsValue::from).collect()
}
/// Iterator over all the devices of the user devices.
pub fn devices(&self) -> Array {
self.inner.devices().map(Device::from).map(JsValue::from).collect()
}
}
@@ -0,0 +1,167 @@
//! Encryption types & siblings.
use std::time::Duration;
use matrix_sdk_common::deserialized_responses::ShieldState as RustShieldState;
use wasm_bindgen::prelude::*;
use crate::events;
/// Settings for an encrypted room.
///
/// This determines the algorithm and rotation periods of a group
/// session.
#[wasm_bindgen(getter_with_clone)]
#[derive(Debug, Clone)]
pub struct EncryptionSettings {
/// The encryption algorithm that should be used in the room.
pub algorithm: EncryptionAlgorithm,
/// How long the session should be used before changing it,
/// expressed in microseconds.
#[wasm_bindgen(js_name = "rotationPeriod")]
pub rotation_period: u64,
/// How many messages should be sent before changing the session.
#[wasm_bindgen(js_name = "rotationPeriodMessages")]
pub rotation_period_messages: u64,
/// The history visibility of the room when the session was
/// created.
#[wasm_bindgen(js_name = "historyVisibility")]
pub history_visibility: events::HistoryVisibility,
/// Should untrusted devices receive the room key, or should they be
/// excluded from the conversation.
#[wasm_bindgen(js_name = "onlyAllowTrustedDevices")]
pub only_allow_trusted_devices: bool,
}
impl Default for EncryptionSettings {
fn default() -> Self {
let default = matrix_sdk_crypto::olm::EncryptionSettings::default();
Self {
algorithm: default.algorithm.into(),
rotation_period: default.rotation_period.as_micros().try_into().unwrap(),
rotation_period_messages: default.rotation_period_msgs,
history_visibility: default.history_visibility.into(),
only_allow_trusted_devices: default.only_allow_trusted_devices,
}
}
}
#[wasm_bindgen]
impl EncryptionSettings {
/// Create a new `EncryptionSettings` with default values.
#[wasm_bindgen(constructor)]
pub fn new() -> EncryptionSettings {
Self::default()
}
}
impl From<&EncryptionSettings> for matrix_sdk_crypto::olm::EncryptionSettings {
fn from(value: &EncryptionSettings) -> Self {
let algorithm = value.algorithm.clone().into();
Self {
algorithm,
rotation_period: Duration::from_micros(value.rotation_period),
rotation_period_msgs: value.rotation_period_messages,
history_visibility: value.history_visibility.clone().into(),
only_allow_trusted_devices: value.only_allow_trusted_devices,
}
}
}
/// An encryption algorithm to be used to encrypt messages sent to a
/// room.
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub enum EncryptionAlgorithm {
/// Olm version 1 using Curve25519, AES-256, and SHA-256.
OlmV1Curve25519AesSha2,
/// Megolm version 1 using AES-256 and SHA-256.
MegolmV1AesSha2,
}
impl From<EncryptionAlgorithm> for JsValue {
fn from(value: EncryptionAlgorithm) -> Self {
use EncryptionAlgorithm::*;
match value {
OlmV1Curve25519AesSha2 => JsValue::from(0),
MegolmV1AesSha2 => JsValue::from(1),
}
}
}
impl From<EncryptionAlgorithm> for matrix_sdk_crypto::types::EventEncryptionAlgorithm {
fn from(value: EncryptionAlgorithm) -> Self {
use EncryptionAlgorithm::*;
match value {
OlmV1Curve25519AesSha2 => Self::OlmV1Curve25519AesSha2,
MegolmV1AesSha2 => Self::MegolmV1AesSha2,
}
}
}
impl From<matrix_sdk_crypto::types::EventEncryptionAlgorithm> for EncryptionAlgorithm {
fn from(value: matrix_sdk_crypto::types::EventEncryptionAlgorithm) -> Self {
use matrix_sdk_crypto::types::EventEncryptionAlgorithm::*;
match value {
OlmV1Curve25519AesSha2 => Self::OlmV1Curve25519AesSha2,
MegolmV1AesSha2 => Self::MegolmV1AesSha2,
_ => unreachable!("Unknown variant"),
}
}
}
/// Take a look at [`matrix_sdk_common::deserialized_responses::ShieldState`]
/// for more info.
#[wasm_bindgen]
#[derive(Debug, Clone, Copy)]
pub enum ShieldColor {
/// Important warning
Red,
/// Low warning
Grey,
/// No warning
None,
}
/// Take a look at [`matrix_sdk_common::deserialized_responses::ShieldState`]
/// for more info.
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct ShieldState {
/// The shield color
pub color: ShieldColor,
message: Option<String>,
}
#[wasm_bindgen]
impl ShieldState {
/// Error message that can be displayed as a tooltip
#[wasm_bindgen(getter)]
pub fn message(&self) -> Option<String> {
self.message.clone()
}
}
impl From<RustShieldState> for ShieldState {
fn from(value: RustShieldState) -> Self {
match value {
RustShieldState::Red { message } => {
Self { color: ShieldColor::Red, message: Some(message.to_owned()) }
}
RustShieldState::Grey { message } => {
Self { color: ShieldColor::Grey, message: Some(message.to_owned()) }
}
RustShieldState::None => Self { color: ShieldColor::None, message: None },
}
}
}
@@ -0,0 +1,61 @@
//! Types related to events.
use ruma::events::room::history_visibility::HistoryVisibility as RumaHistoryVisibility;
use wasm_bindgen::prelude::*;
/// Who can see a room's history.
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub enum HistoryVisibility {
/// Previous events are accessible to newly joined members from
/// the point they were invited onwards.
///
/// Events stop being accessible when the member's state changes
/// to something other than *invite* or *join*.
Invited,
/// Previous events are accessible to newly joined members from
/// the point they joined the room onwards.
///
/// Events stop being accessible when the member's state changes
/// to something other than *join*.
Joined,
/// Previous events are always accessible to newly joined members.
///
/// All events in the room are accessible, even those sent when
/// the member was not a part of the room.
Shared,
/// All events while this is the `HistoryVisibility` value may be
/// shared by any participating homeserver with anyone, regardless
/// of whether they have ever joined the room.
WorldReadable,
}
impl From<HistoryVisibility> for RumaHistoryVisibility {
fn from(value: HistoryVisibility) -> Self {
use HistoryVisibility::*;
match value {
Invited => Self::Invited,
Joined => Self::Joined,
Shared => Self::Shared,
WorldReadable => Self::WorldReadable,
}
}
}
impl From<RumaHistoryVisibility> for HistoryVisibility {
fn from(value: RumaHistoryVisibility) -> Self {
use RumaHistoryVisibility::*;
match value {
Invited => Self::Invited,
Joined => Self::Joined,
Shared => Self::Shared,
WorldReadable => Self::WorldReadable,
_ => unreachable!("Unknown variant"),
}
}
}
@@ -0,0 +1,26 @@
use std::future::Future;
use js_sys::Promise;
use wasm_bindgen::{JsError, JsValue, UnwrapThrowExt};
use wasm_bindgen_futures::spawn_local;
pub(crate) fn future_to_promise<F, T>(future: F) -> Promise
where
F: Future<Output = Result<T, anyhow::Error>> + 'static,
T: Into<JsValue>,
{
let mut future = Some(future);
Promise::new(&mut |resolve, reject| {
let future = future.take().unwrap_throw();
spawn_local(async move {
match future.await {
Ok(value) => resolve.call1(&JsValue::UNDEFINED, &value.into()).unwrap_throw(),
Err(value) => reject
.call1(&JsValue::UNDEFINED, &JsError::new(&value.to_string()).into())
.unwrap_throw(),
};
});
})
}
@@ -0,0 +1,321 @@
//! Types for [Matrix](https://matrix.org/) identifiers for devices,
//! events, keys, rooms, servers, users and URIs.
use wasm_bindgen::prelude::*;
use crate::impl_from_to_inner;
/// A Matrix [user ID].
///
/// [user ID]: https://spec.matrix.org/v1.2/appendices/#user-identifiers
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct UserId {
pub(crate) inner: ruma::OwnedUserId,
}
impl_from_to_inner!(ruma::OwnedUserId => UserId);
#[wasm_bindgen]
impl UserId {
/// Parse/validate and create a new `UserId`.
#[wasm_bindgen(constructor)]
pub fn new(id: &str) -> Result<UserId, JsError> {
Ok(Self::from(ruma::UserId::parse(id)?))
}
/// Returns the user's localpart.
#[wasm_bindgen(getter)]
pub fn localpart(&self) -> String {
self.inner.localpart().to_owned()
}
/// Returns the server name of the user ID.
#[wasm_bindgen(getter, js_name = "serverName")]
pub fn server_name(&self) -> ServerName {
ServerName { inner: self.inner.server_name().to_owned() }
}
/// Whether this user ID is a historical one.
///
/// A historical user ID is one that doesn't conform to the latest
/// specification of the user ID grammar but is still accepted
/// because it was previously allowed.
#[wasm_bindgen(js_name = "isHistorical")]
pub fn is_historical(&self) -> bool {
self.inner.is_historical()
}
/// Return the user ID as a string.
#[wasm_bindgen(js_name = "toString")]
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
self.inner.as_str().to_owned()
}
}
/// A Matrix key ID.
///
/// Device identifiers in Matrix are completely opaque character
/// sequences. This type is provided simply for its semantic value.
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct DeviceId {
pub(crate) inner: ruma::OwnedDeviceId,
}
impl_from_to_inner!(ruma::OwnedDeviceId => DeviceId);
#[wasm_bindgen]
impl DeviceId {
/// Create a new `DeviceId`.
#[wasm_bindgen(constructor)]
pub fn new(id: &str) -> DeviceId {
Self::from(ruma::OwnedDeviceId::from(id))
}
/// Return the device ID as a string.
#[wasm_bindgen(js_name = "toString")]
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
self.inner.as_str().to_owned()
}
}
/// A Matrix device key ID.
///
/// A key algorithm and a device ID, combined with a :.
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct DeviceKeyId {
pub(crate) inner: ruma::OwnedDeviceKeyId,
}
impl_from_to_inner!(ruma::OwnedDeviceKeyId => DeviceKeyId);
#[wasm_bindgen]
impl DeviceKeyId {
/// Parse/validate and create a new `DeviceKeyId`.
#[wasm_bindgen(constructor)]
pub fn new(id: String) -> Result<DeviceKeyId, JsError> {
Ok(Self::from(ruma::DeviceKeyId::parse(id.as_str())?))
}
/// Returns key algorithm of the device key ID.
#[wasm_bindgen(getter)]
pub fn algorithm(&self) -> DeviceKeyAlgorithm {
self.inner.algorithm().into()
}
/// Returns device ID of the device key ID.
#[wasm_bindgen(getter, js_name = "deviceId")]
pub fn device_id(&self) -> DeviceId {
self.inner.device_id().to_owned().into()
}
/// Return the device key ID as a string.
#[wasm_bindgen(js_name = "toString")]
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
self.inner.to_string()
}
}
/// The basic key algorithms in the specification.
#[wasm_bindgen]
#[derive(Debug)]
pub struct DeviceKeyAlgorithm {
pub(crate) inner: ruma::DeviceKeyAlgorithm,
}
impl_from_to_inner!(ruma::DeviceKeyAlgorithm => DeviceKeyAlgorithm);
#[wasm_bindgen]
impl DeviceKeyAlgorithm {
/// Read the device key algorithm's name. If the name is
/// `Unknown`, one may be interested by the `to_string` method to
/// read the original name.
#[wasm_bindgen(getter)]
pub fn name(&self) -> DeviceKeyAlgorithmName {
self.inner.clone().into()
}
/// Return the device key algorithm as a string.
#[wasm_bindgen(js_name = "toString")]
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
self.inner.to_string()
}
}
/// The basic key algorithm names in the specification.
#[wasm_bindgen]
#[derive(Debug)]
pub enum DeviceKeyAlgorithmName {
/// The Ed25519 signature algorithm.
Ed25519,
/// The Curve25519 ECDH algorithm.
Curve25519,
/// The Curve25519 ECDH algorithm, but the key also contains
/// signatures.
SignedCurve25519,
/// An unknown device key algorithm.
Unknown,
}
impl TryFrom<DeviceKeyAlgorithmName> for ruma::DeviceKeyAlgorithm {
type Error = JsError;
fn try_from(value: DeviceKeyAlgorithmName) -> Result<Self, Self::Error> {
use DeviceKeyAlgorithmName::*;
Ok(match value {
Ed25519 => Self::Ed25519,
Curve25519 => Self::Curve25519,
SignedCurve25519 => Self::SignedCurve25519,
Unknown => {
return Err(JsError::new(
"The `DeviceKeyAlgorithmName.Unknown` variant cannot be converted",
))
}
})
}
}
impl From<ruma::DeviceKeyAlgorithm> for DeviceKeyAlgorithmName {
fn from(value: ruma::DeviceKeyAlgorithm) -> Self {
use ruma::DeviceKeyAlgorithm::*;
match value {
Ed25519 => Self::Ed25519,
Curve25519 => Self::Curve25519,
SignedCurve25519 => Self::SignedCurve25519,
_ => Self::Unknown,
}
}
}
/// A Matrix [room ID].
///
/// [room ID]: https://spec.matrix.org/v1.2/appendices/#room-ids-and-event-ids
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct RoomId {
pub(crate) inner: ruma::OwnedRoomId,
}
impl_from_to_inner!(ruma::OwnedRoomId => RoomId);
#[wasm_bindgen]
impl RoomId {
/// Parse/validate and create a new `RoomId`.
#[wasm_bindgen(constructor)]
pub fn new(id: &str) -> Result<RoomId, JsError> {
Ok(Self::from(ruma::RoomId::parse(id)?))
}
/// Returns the user's localpart.
#[wasm_bindgen(getter)]
pub fn localpart(&self) -> String {
self.inner.localpart().to_owned()
}
/// Returns the server name of the room ID.
#[wasm_bindgen(getter, js_name = "serverName")]
pub fn server_name(&self) -> ServerName {
ServerName { inner: self.inner.server_name().to_owned() }
}
/// Return the room ID as a string.
#[wasm_bindgen(js_name = "toString")]
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
self.inner.as_str().to_owned()
}
}
/// A Matrix-spec compliant [server name].
///
/// It consists of a host and an optional port (separated by a colon if
/// present).
///
/// [server name]: https://spec.matrix.org/v1.2/appendices/#server-name
#[wasm_bindgen]
#[derive(Debug)]
pub struct ServerName {
inner: ruma::OwnedServerName,
}
#[wasm_bindgen]
impl ServerName {
/// Parse/validate and create a new `ServerName`.
#[wasm_bindgen(constructor)]
pub fn new(name: &str) -> Result<ServerName, JsError> {
Ok(Self { inner: ruma::ServerName::parse(name)? })
}
/// Returns the host of the server name.
///
/// That is: Return the part of the server before `:<port>` or the
/// full server name if there is no port.
#[wasm_bindgen(getter)]
pub fn host(&self) -> String {
self.inner.host().to_owned()
}
/// Returns the port of the server name if any.
#[wasm_bindgen(getter)]
pub fn port(&self) -> Option<u16> {
self.inner.port()
}
/// Returns true if and only if the server name is an IPv4 or IPv6
/// address.
#[wasm_bindgen(js_name = "isIpLiteral")]
pub fn is_ip_literal(&self) -> bool {
self.inner.is_ip_literal()
}
}
/// A Matrix [event ID].
///
/// An `EventId` is generated randomly or converted from a string
/// slice, and can be converted back into a string as needed.
///
/// [event ID]: https://spec.matrix.org/v1.2/appendices/#room-ids-and-event-ids
#[wasm_bindgen]
#[derive(Debug)]
pub struct EventId {
pub(crate) inner: ruma::OwnedEventId,
}
#[wasm_bindgen]
impl EventId {
/// Parse/validate and create a new `EventId`.
#[wasm_bindgen(constructor)]
pub fn new(id: &str) -> Result<EventId, JsError> {
Ok(Self { inner: <&ruma::EventId>::try_from(id)?.to_owned() })
}
/// Returns the event's localpart.
#[wasm_bindgen(getter)]
pub fn localpart(&self) -> String {
self.inner.localpart().to_owned()
}
/// Returns the server name of the event ID.
#[wasm_bindgen(getter, js_name = "serverName")]
pub fn server_name(&self) -> Option<ServerName> {
Some(ServerName { inner: self.inner.server_name()?.to_owned() })
}
/// Return the event ID as a string.
#[wasm_bindgen(js_name = "toString")]
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
self.inner.as_str().to_owned()
}
}
@@ -0,0 +1,172 @@
//! User identities.
use js_sys::{Array, Promise};
use wasm_bindgen::prelude::*;
use crate::{
future::future_to_promise, identifiers, impl_from_to_inner, js::try_array_to_vec, requests,
verification,
};
pub(crate) struct UserIdentities {
inner: matrix_sdk_crypto::UserIdentities,
}
impl_from_to_inner!(matrix_sdk_crypto::UserIdentities => UserIdentities);
impl From<UserIdentities> for JsValue {
fn from(user_identities: UserIdentities) -> Self {
use matrix_sdk_crypto::UserIdentities::*;
match user_identities.inner {
Own(own) => JsValue::from(OwnUserIdentity::from(own)),
Other(other) => JsValue::from(UserIdentity::from(other)),
}
}
}
/// Struct representing a cross signing identity of a user.
///
/// This is the user identity of a user that is our own.
#[wasm_bindgen]
#[derive(Debug)]
pub struct OwnUserIdentity {
inner: matrix_sdk_crypto::OwnUserIdentity,
}
impl_from_to_inner!(matrix_sdk_crypto::OwnUserIdentity => OwnUserIdentity);
#[wasm_bindgen]
impl OwnUserIdentity {
/// Mark our user identity as verified.
///
/// This will mark the identity locally as verified and sign it with our own
/// device.
///
/// Returns a signature upload request that needs to be sent out.
pub fn verify(&self) -> Promise {
let me = self.inner.clone();
future_to_promise(async move {
Ok(requests::SignatureUploadRequest::try_from(&me.verify().await?)?)
})
}
/// Send a verification request to our other devices.
#[wasm_bindgen(js_name = "requestVerification")]
pub fn request_verification(&self, methods: Option<Array>) -> Result<Promise, JsError> {
let methods =
methods.map(try_array_to_vec::<verification::VerificationMethod, _>).transpose()?;
let me = self.inner.clone();
Ok(future_to_promise(async move {
let tuple = Array::new();
let (verification_request, outgoing_verification_request) = match methods {
Some(methods) => me.request_verification_with_methods(methods).await?,
None => me.request_verification().await?,
};
tuple.set(0, verification::VerificationRequest::from(verification_request).into());
tuple.set(
1,
verification::OutgoingVerificationRequest::from(outgoing_verification_request)
.try_into()?,
);
Ok(tuple)
}))
}
/// Does our user identity trust our own device, i.e. have we signed our own
/// device keys with our self-signing key?
#[wasm_bindgen(js_name = "trustsOurOwnDevice")]
pub fn trusts_our_own_device(&self) -> Promise {
let me = self.inner.clone();
future_to_promise(async move { Ok(me.trusts_our_own_device().await?) })
}
}
/// Struct representing a cross signing identity of a user.
///
/// This is the user identity of a user that isn't our own. Other users will
/// only contain a master key and a self signing key, meaning that only device
/// signatures can be checked with this identity.
///
/// This struct wraps a read-only version of the struct and allows verifications
/// to be requested to verify our own device with the user identity.
#[wasm_bindgen]
#[derive(Debug)]
pub struct UserIdentity {
inner: matrix_sdk_crypto::UserIdentity,
}
impl_from_to_inner!(matrix_sdk_crypto::UserIdentity => UserIdentity);
#[wasm_bindgen]
impl UserIdentity {
/// Is this user identity verified?
#[wasm_bindgen(js_name = "isVerified")]
pub fn is_verified(&self) -> bool {
self.inner.is_verified()
}
/// Manually verify this user.
///
/// This method will attempt to sign the user identity using our private
/// cross signing key.
///
/// This method fails if we don't have the private part of our user-signing
/// key.
///
/// Returns a request that needs to be sent out for the user to be marked as
/// verified.
pub fn verify(&self) -> Promise {
let me = self.inner.clone();
future_to_promise(async move {
Ok(requests::SignatureUploadRequest::try_from(&me.verify().await?)?)
})
}
/// Create a `VerificationRequest` object after the verification
/// request content has been sent out.
#[wasm_bindgen(js_name = "requestVerification")]
pub fn request_verification(
&self,
room_id: &identifiers::RoomId,
request_event_id: &identifiers::EventId,
methods: Option<Array>,
) -> Result<Promise, JsError> {
let me = self.inner.clone();
let room_id = room_id.inner.clone();
let request_event_id = request_event_id.inner.clone();
let methods =
methods.map(try_array_to_vec::<verification::VerificationMethod, _>).transpose()?;
Ok(future_to_promise::<_, verification::VerificationRequest>(async move {
Ok(me
.request_verification(room_id.as_ref(), request_event_id.as_ref(), methods)
.await
.into())
}))
}
/// Send a verification request to the given user.
///
/// The returned content needs to be sent out into a DM room with the given
/// user.
///
/// After the content has been sent out a VerificationRequest can be started
/// with the `request_verification` method.
#[wasm_bindgen(js_name = "verificationRequestContent")]
pub fn verification_request_content(&self, methods: Option<Array>) -> Result<Promise, JsError> {
let me = self.inner.clone();
let methods =
methods.map(try_array_to_vec::<verification::VerificationMethod, _>).transpose()?;
Ok(future_to_promise(async move {
Ok(serde_json::to_string(&me.verification_request_content(methods).await)?)
}))
}
}
+40
View File
@@ -0,0 +1,40 @@
use js_sys::{Array, Object, Reflect};
use wasm_bindgen::{convert::RefFromWasmAbi, prelude::*};
/// A really hacky and dirty code to downcast a `JsValue` to `T:
/// RefFromWasmAbi`, inspired by
/// https://github.com/rustwasm/wasm-bindgen/issues/2231#issuecomment-656293288.
///
/// The returned value is likely to be a `wasm_bindgen::__ref::Ref<T>`.
pub(crate) fn downcast<T>(value: &JsValue, classname: &str) -> Result<T::Anchor, JsError>
where
T: RefFromWasmAbi<Abi = u32>,
{
let constructor_name = Object::get_prototype_of(value).constructor().name();
if constructor_name == classname {
let pointer = Reflect::get(value, &JsValue::from_str("ptr"))
.map_err(|_| JsError::new("Failed to read the `JsValue` pointer"))?;
let pointer = pointer
.as_f64()
.ok_or_else(|| JsError::new("Failed to read the `JsValue` pointer as a `f64`"))?
as u32;
Ok(unsafe { T::ref_from_abi(pointer) })
} else {
Err(JsError::new(&format!(
"Expect an `{classname}` instance, received `{constructor_name}` instead",
)))
}
}
/// Transform a value `JS` from JavaScript to a Rust wrapper, to a
/// Rust wrapped type.
pub(crate) fn try_array_to_vec<JS, Rust>(
array: Array,
) -> Result<Vec<Rust>, <JS as TryFrom<JsValue>>::Error>
where
JS: TryFrom<JsValue> + Into<Rust>,
{
array.iter().map(|item| JS::try_from(item).map(Into::into)).collect()
}
+73
View File
@@ -0,0 +1,73 @@
// Copyright 2022 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![warn(missing_docs, missing_debug_implementations)]
// triggered by wasm_bindgen code
#![allow(clippy::drop_non_drop)]
pub mod attachment;
pub mod device;
pub mod encryption;
pub mod events;
mod future;
pub mod identifiers;
pub mod identities;
mod js;
pub mod machine;
mod macros;
pub mod olm;
pub mod requests;
pub mod responses;
pub mod store;
pub mod sync_events;
mod tracing;
pub mod types;
pub mod verification;
pub mod vodozemac;
use js_sys::JsString;
use wasm_bindgen::prelude::*;
/// Object containing the versions of the Rust libraries we are using.
#[wasm_bindgen(getter_with_clone)]
#[derive(Debug)]
pub struct Versions {
/// The version of the vodozemac crate.
#[wasm_bindgen(readonly)]
pub vodozemac: JsString,
/// The version of the matrix-sdk-crypto crate.
#[wasm_bindgen(readonly)]
pub matrix_sdk_crypto: JsString,
}
/// Get the versions of the Rust libraries we are using.
#[wasm_bindgen(js_name = "getVersions")]
pub fn get_versions() -> Versions {
Versions {
vodozemac: matrix_sdk_crypto::vodozemac::VERSION.into(),
matrix_sdk_crypto: matrix_sdk_crypto::VERSION.into(),
}
}
/// Run some stuff when the Wasm module is instantiated.
///
/// Right now, it does the following:
///
/// * Redirect Rust panics to JavaScript console.
#[wasm_bindgen(start)]
pub fn start() {
console_error_panic_hook::set_once();
}
@@ -0,0 +1,843 @@
//! The crypto specific Olm objects.
use std::{collections::BTreeMap, ops::Deref, time::Duration};
use futures_util::StreamExt;
use js_sys::{Array, Function, Map, Promise, Set};
use ruma::{serde::Raw, DeviceKeyAlgorithm, OwnedTransactionId, UInt};
use serde_json::{json, Value as JsonValue};
use tracing::warn;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::{spawn_local, JsFuture};
use crate::{
device, encryption,
future::future_to_promise,
identifiers, identities,
js::downcast,
olm, requests,
requests::{OutgoingRequest, ToDeviceRequest},
responses::{self, response_from_string},
store,
store::RoomKeyInfo,
sync_events, types, verification, vodozemac,
};
/// State machine implementation of the Olm/Megolm encryption protocol
/// used for Matrix end to end encryption.
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct OlmMachine {
inner: matrix_sdk_crypto::OlmMachine,
}
#[wasm_bindgen]
impl OlmMachine {
/// Constructor will always fail. To create a new `OlmMachine`, please use
/// the `initialize` method.
///
/// Why this pattern? `initialize` returns a `Promise`. Returning a
// `Promise` from a constructor is not idiomatic in JavaScript.
#[wasm_bindgen(constructor)]
pub fn new() -> Result<OlmMachine, JsError> {
Err(JsError::new("To build an `OlmMachine`, please use the `initialize` method"))
}
/// Create a new `OlmMachine`.
///
/// The created machine will keep the encryption keys either in a IndexedDB
/// based store, or in a memory store and once the objects is dropped,
/// the keys will be lost.
///
/// # Arguments
///
/// * `user_id` - represents the unique ID of the user that owns this
/// machine.
///
/// * `device_id` - represents the unique ID of the device
/// that owns this machine.
///
/// * `store_name` - The name that should be used to open the IndexedDB
/// based database. If this isn't provided, a memory-only store will be
/// used. *Note* the memory-only store will lose your E2EE keys when the
/// `OlmMachine` gets dropped.
///
/// * `store_passphrase` - The passphrase that should be used to encrypt the
/// IndexedDB based
pub fn initialize(
user_id: &identifiers::UserId,
device_id: &identifiers::DeviceId,
store_name: Option<String>,
store_passphrase: Option<String>,
) -> Promise {
let user_id = user_id.inner.clone();
let device_id = device_id.inner.clone();
future_to_promise(async move {
let store = match (store_name, store_passphrase) {
(Some(store_name), Some(mut store_passphrase)) => {
use zeroize::Zeroize;
let store = Some(
matrix_sdk_indexeddb::IndexeddbCryptoStore::open_with_passphrase(
&store_name,
&store_passphrase,
)
.await?,
);
store_passphrase.zeroize();
store
}
(Some(store_name), None) => Some(
matrix_sdk_indexeddb::IndexeddbCryptoStore::open_with_name(&store_name).await?,
),
(None, Some(_)) => {
return Err(anyhow::Error::msg(
"The `store_passphrase` has been set, but it has an effect only if \
`store_name` is set, which is not; please provide one",
))
}
(None, None) => None,
};
Ok(OlmMachine {
inner: match store {
// We need this `#[cfg]` because `IndexeddbCryptoStore`
// implements `CryptoStore` only on `target_arch =
// "wasm32"`. Without that, we could have a compilation
// error when checking the entire workspace. In practice,
// it doesn't impact this crate because it's always
// compiled for `wasm32`.
#[cfg(target_arch = "wasm32")]
Some(store) => {
matrix_sdk_crypto::OlmMachine::with_store(
user_id.as_ref(),
device_id.as_ref(),
store,
)
.await?
}
_ => {
matrix_sdk_crypto::OlmMachine::new(user_id.as_ref(), device_id.as_ref())
.await
}
},
})
})
}
/// The unique user ID that owns this `OlmMachine` instance.
#[wasm_bindgen(getter, js_name = "userId")]
pub fn user_id(&self) -> identifiers::UserId {
identifiers::UserId::from(self.inner.user_id().to_owned())
}
/// The unique device ID that identifies this `OlmMachine`.
#[wasm_bindgen(getter, js_name = "deviceId")]
pub fn device_id(&self) -> identifiers::DeviceId {
identifiers::DeviceId::from(self.inner.device_id().to_owned())
}
/// Get the public parts of our Olm identity keys.
#[wasm_bindgen(getter, js_name = "identityKeys")]
pub fn identity_keys(&self) -> vodozemac::IdentityKeys {
self.inner.identity_keys().into()
}
/// Get the display name of our own device.
#[wasm_bindgen(getter, js_name = "displayName")]
pub fn display_name(&self) -> Promise {
let me = self.inner.clone();
future_to_promise(async move { Ok(me.display_name().await?) })
}
/// Get the list of users whose devices we are currently tracking.
///
/// A user can be marked for tracking using the
/// [`update_tracked_users`](#method.update_tracked_users) method.
///
/// Returns a `Set<UserId>`.
#[wasm_bindgen(js_name = "trackedUsers")]
pub fn tracked_users(&self) -> Result<Promise, JsError> {
let set = Set::new(&JsValue::UNDEFINED);
let me = self.inner.clone();
Ok(future_to_promise(async move {
for user in me.tracked_users().await? {
set.add(&identifiers::UserId::from(user).into());
}
Ok(set)
}))
}
/// Update the list of tracked users.
///
/// The OlmMachine maintains a list of users whose devices we are keeping
/// track of: these are known as "tracked users". These must be users
/// that we share a room with, so that the server sends us updates for
/// their device lists.
///
/// # Arguments
///
/// * `users` - An array of user ids that should be added to the list of
/// tracked users
///
/// Any users that hadn't been seen before will be flagged for a key query
/// immediately, and whenever `receive_sync_changes` receives a
/// "changed" notification for that user in the future.
///
/// Users that were already in the list are unaffected.
#[wasm_bindgen(js_name = "updateTrackedUsers")]
pub fn update_tracked_users(&self, users: &Array) -> Result<Promise, JsError> {
let users = users
.iter()
.map(|user| Ok(downcast::<identifiers::UserId>(&user, "UserId")?.inner.clone()))
.collect::<Result<Vec<ruma::OwnedUserId>, JsError>>()?;
let me = self.inner.clone();
Ok(future_to_promise(async move {
me.update_tracked_users(users.iter().map(AsRef::as_ref)).await?;
Ok(JsValue::UNDEFINED)
}))
}
/// Handle to-device events and one-time key counts from a sync
/// response.
///
/// This will decrypt and handle to-device events returning the
/// decrypted versions of them.
///
/// To decrypt an event from the room timeline call
/// `decrypt_room_event`.
#[wasm_bindgen(js_name = "receiveSyncChanges")]
pub fn receive_sync_changes(
&self,
to_device_events: &str,
changed_devices: &sync_events::DeviceLists,
one_time_key_counts: &Map,
unused_fallback_keys: &Set,
) -> Result<Promise, JsError> {
let to_device_events = serde_json::from_str(to_device_events)?;
let changed_devices = changed_devices.inner.clone();
let one_time_key_counts: BTreeMap<DeviceKeyAlgorithm, UInt> = one_time_key_counts
.entries()
.into_iter()
.filter_map(|js_value| {
let pair = Array::from(&js_value.ok()?);
let (key, value) = (
DeviceKeyAlgorithm::from(pair.at(0).as_string()?),
UInt::new(pair.at(1).as_f64()? as u64)?,
);
Some((key, value))
})
.collect();
let unused_fallback_keys: Option<Vec<DeviceKeyAlgorithm>> = Some(
unused_fallback_keys
.values()
.into_iter()
.filter_map(|js_value| Some(DeviceKeyAlgorithm::from(js_value.ok()?.as_string()?)))
.collect(),
);
let me = self.inner.clone();
Ok(future_to_promise(async move {
Ok(serde_json::to_string(
&me.receive_sync_changes(
to_device_events,
&changed_devices,
&one_time_key_counts,
unused_fallback_keys.as_deref(),
)
.await?,
)?)
}))
}
/// Get the outgoing requests that need to be sent out.
///
/// This returns a list of `JsValue` to represent either:
/// * `KeysUploadRequest`,
/// * `KeysQueryRequest`,
/// * `KeysClaimRequest`,
/// * `ToDeviceRequest`,
/// * `SignatureUploadRequest`,
/// * `RoomMessageRequest` or
/// * `KeysBackupRequest`.
///
/// Those requests need to be sent out to the server and the
/// responses need to be passed back to the state machine using
/// `mark_request_as_sent`.
#[wasm_bindgen(js_name = "outgoingRequests")]
pub fn outgoing_requests(&self) -> Promise {
let me = self.inner.clone();
future_to_promise(async move {
Ok(me
.outgoing_requests()
.await?
.into_iter()
.map(OutgoingRequest)
.map(TryFrom::try_from)
.collect::<Result<Vec<JsValue>, _>>()?
.into_iter()
.collect::<Array>())
})
}
/// Mark the request with the given request ID as sent (see
/// `outgoing_requests`).
///
/// Arguments are:
///
/// * `request_id` represents the unique ID of the request that was sent
/// out. This is needed to couple the response with the now sent out
/// request.
/// * `response_type` represents the type of the request that was sent out.
/// * `response` represents the response that was received from the server
/// after the outgoing request was sent out.
#[wasm_bindgen(js_name = "markRequestAsSent")]
pub fn mark_request_as_sent(
&self,
request_id: &str,
request_type: requests::RequestType,
response: &str,
) -> Result<Promise, JsError> {
let transaction_id = OwnedTransactionId::from(request_id);
let response = response_from_string(response)?;
let incoming_response = responses::OwnedResponse::try_from((request_type, response))?;
let me = self.inner.clone();
Ok(future_to_promise(async move {
Ok(me.mark_request_as_sent(&transaction_id, &incoming_response).await.map(|_| true)?)
}))
}
/// Encrypt a room message for the given room.
///
/// Beware that a room key needs to be shared before this
/// method can be called using the `share_room_key` method.
///
/// `room_id` is the ID of the room for which the message should
/// be encrypted. `event_type` is the type of the event. `content`
/// is the plaintext content of the message that should be
/// encrypted.
///
/// # Panics
///
/// Panics if a group session for the given room wasn't shared
/// beforehand.
#[wasm_bindgen(js_name = "encryptRoomEvent")]
pub fn encrypt_room_event(
&self,
room_id: &identifiers::RoomId,
event_type: String,
content: &str,
) -> Result<Promise, JsError> {
let room_id = room_id.inner.clone();
let content: JsonValue = serde_json::from_str(content)?;
let me = self.inner.clone();
Ok(future_to_promise(async move {
Ok(serde_json::to_string(
&me.encrypt_room_event_raw(&room_id, content, event_type.as_ref()).await?,
)?)
}))
}
/// Decrypt an event from a room timeline.
///
/// # Arguments
///
/// * `event`, the event that should be decrypted.
/// * `room_id`, the ID of the room where the event was sent to.
#[wasm_bindgen(js_name = "decryptRoomEvent")]
pub fn decrypt_room_event(
&self,
event: &str,
room_id: &identifiers::RoomId,
) -> Result<Promise, JsError> {
let event: Raw<_> = serde_json::from_str(event)?;
let room_id = room_id.inner.clone();
let me = self.inner.clone();
Ok(future_to_promise(async move {
let room_event = me.decrypt_room_event(&event, room_id.as_ref()).await?;
Ok(responses::DecryptedRoomEvent::from(room_event))
}))
}
/// Get the status of the private cross signing keys.
///
/// This can be used to check which private cross signing keys we
/// have stored locally.
#[wasm_bindgen(js_name = "crossSigningStatus")]
pub fn cross_signing_status(&self) -> Promise {
let me = self.inner.clone();
future_to_promise::<_, olm::CrossSigningStatus>(async move {
Ok(me.cross_signing_status().await.into())
})
}
/// Export all the private cross signing keys we have.
///
/// The export will contain the seed for the ed25519 keys as a
/// unpadded base64 encoded string.
///
/// This method returns None if we dont have any private cross
/// signing keys.
#[wasm_bindgen(js_name = "exportCrossSigningKeys")]
pub fn export_cross_signing_keys(&self) -> Promise {
let me = self.inner.clone();
future_to_promise(async move {
Ok(me.export_cross_signing_keys().await.map(store::CrossSigningKeyExport::from))
})
}
/// Import our private cross signing keys.
///
/// The export needs to contain the seed for the ed25519 keys as
/// an unpadded base64 encoded string.
#[wasm_bindgen(js_name = "importCrossSigningKeys")]
pub fn import_cross_signing_keys(&self, export: store::CrossSigningKeyExport) -> Promise {
let me = self.inner.clone();
let export = export.inner;
future_to_promise(async move {
Ok(me.import_cross_signing_keys(export).await.map(olm::CrossSigningStatus::from)?)
})
}
/// Create a new cross signing identity and get the upload request
/// to push the new public keys to the server.
///
/// Warning: This will delete any existing cross signing keys that
/// might exist on the server and thus will reset the trust
/// between all the devices.
///
/// Uploading these keys will require user interactive auth.
#[wasm_bindgen(js_name = "bootstrapCrossSigning")]
pub fn bootstrap_cross_signing(&self, reset: bool) -> Promise {
let me = self.inner.clone();
future_to_promise(async move {
let (upload_signing_keys_request, upload_signatures_request) =
me.bootstrap_cross_signing(reset).await?;
let tuple = Array::new();
tuple.set(
0,
requests::SigningKeysUploadRequest::try_from(&upload_signing_keys_request)?.into(),
);
tuple.set(
1,
requests::SignatureUploadRequest::try_from(&upload_signatures_request)?.into(),
);
Ok(tuple)
})
}
/// Get the cross signing user identity of a user.
#[wasm_bindgen(js_name = "getIdentity")]
pub fn get_identity(&self, user_id: &identifiers::UserId) -> Promise {
let me = self.inner.clone();
let user_id = user_id.inner.clone();
future_to_promise(async move {
Ok(me.get_identity(user_id.as_ref(), None).await?.map(identities::UserIdentities::from))
})
}
/// Sign the given message using our device key and if available
/// cross-signing master key.
pub fn sign(&self, message: String) -> Promise {
let me = self.inner.clone();
future_to_promise::<_, types::Signatures>(async move { Ok(me.sign(&message).await.into()) })
}
/// Invalidate the currently active outbound group session for the
/// given room.
///
/// Returns true if a session was invalidated, false if there was
/// no session to invalidate.
#[wasm_bindgen(js_name = "invalidateGroupSession")]
pub fn invalidate_group_session(&self, room_id: &identifiers::RoomId) -> Promise {
let room_id = room_id.inner.clone();
let me = self.inner.clone();
future_to_promise(async move { Ok(me.invalidate_group_session(&room_id).await?) })
}
/// Get to-device requests to share a room key with users in a room.
///
/// `room_id` is the room ID. `users` is an array of `UserId`
/// objects. `encryption_settings` are an `EncryptionSettings`
/// object.
///
/// Returns an array of `ToDeviceRequest`s.
#[wasm_bindgen(js_name = "shareRoomKey")]
pub fn share_room_key(
&self,
room_id: &identifiers::RoomId,
users: &Array,
encryption_settings: &encryption::EncryptionSettings,
) -> Result<Promise, JsError> {
let room_id = room_id.inner.clone();
let users = users
.iter()
.map(|user| Ok(downcast::<identifiers::UserId>(&user, "UserId")?.inner.clone()))
.collect::<Result<Vec<ruma::OwnedUserId>, JsError>>()?;
let encryption_settings =
matrix_sdk_crypto::olm::EncryptionSettings::from(encryption_settings);
let me = self.inner.clone();
Ok(future_to_promise(async move {
let to_device_requests = me
.share_room_key(&room_id, users.iter().map(AsRef::as_ref), encryption_settings)
.await?;
// convert each request to our own ToDeviceRequest struct, and then wrap it in a
// JsValue.
//
// Then collect the results into a javascript Array, throwing any errors into
// the promise.
Ok(to_device_requests
.into_iter()
.map(|td| ToDeviceRequest::try_from(td.deref()).map(JsValue::from))
.collect::<Result<Array, _>>()?)
}))
}
/// Get the a key claiming request for the user/device pairs that
/// we are missing Olm sessions for.
///
/// Returns `null` if no key claiming request needs to be sent
/// out, otherwise it returns a `KeysClaimRequest` object.
///
/// Sessions need to be established between devices so group
/// sessions for a room can be shared with them.
///
/// This should be called every time a group session needs to be
/// shared as well as between sync calls. After a sync some
/// devices may request room keys without us having a valid Olm
/// session with them, making it impossible to server the room key
/// request, thus its necessary to check for missing sessions
/// between sync as well.
///
/// Note: Care should be taken that only one such request at a
/// time is in flight, e.g. using a lock.
///
/// The response of a successful key claiming requests needs to be
/// passed to the `OlmMachine` with the `mark_request_as_sent`.
///
/// `users` represents the list of users that we should check if
/// we lack a session with one of their devices. This can be an
/// empty iterator when calling this method between sync requests.
#[wasm_bindgen(js_name = "getMissingSessions")]
pub fn get_missing_sessions(&self, users: &Array) -> Result<Promise, JsError> {
let users = users
.iter()
.map(|user| Ok(downcast::<identifiers::UserId>(&user, "UserId")?.inner.clone()))
.collect::<Result<Vec<ruma::OwnedUserId>, JsError>>()?;
let me = self.inner.clone();
Ok(future_to_promise(async move {
match me.get_missing_sessions(users.iter().map(AsRef::as_ref)).await? {
Some((transaction_id, keys_claim_request)) => {
Ok(JsValue::from(requests::KeysClaimRequest::try_from((
transaction_id.to_string(),
&keys_claim_request,
))?))
}
None => Ok(JsValue::NULL),
}
}))
}
/// Get a map holding all the devices of a user.
///
/// `user_id` represents the unique ID of the user that the
/// devices belong to.
#[wasm_bindgen(js_name = "getUserDevices")]
pub fn get_user_devices(&self, user_id: &identifiers::UserId) -> Promise {
let user_id = user_id.inner.clone();
let me = self.inner.clone();
future_to_promise::<_, device::UserDevices>(async move {
// wait for up to a second for any in-flight device list requests to complete.
// The reason for this isn't so much to avoid races (some level of raciness is
// inevitable for this method) but to make testing easier.
Ok(me.get_user_devices(&user_id, Some(Duration::from_secs(1))).await.map(Into::into)?)
})
}
/// Get a specific device of a user if one is found and the crypto store
/// didn't throw an error.
///
/// `user_id` represents the unique ID of the user that the
/// identity belongs to. `device_id` represents the unique ID of
/// the device.
#[wasm_bindgen(js_name = "getDevice")]
pub fn get_device(
&self,
user_id: &identifiers::UserId,
device_id: &identifiers::DeviceId,
) -> Promise {
let user_id = user_id.inner.clone();
let device_id = device_id.inner.clone();
let me = self.inner.clone();
future_to_promise::<_, Option<device::Device>>(async move {
Ok(me.get_device(&user_id, &device_id, None).await?.map(Into::into))
})
}
/// Get a verification object for the given user ID with the given
/// flow ID (a to-device request ID if the verification has been
/// requested by a to-device request, or a room event ID if the
/// verification has been requested by a room event).
///
/// It returns a “`Verification` object”, which is either a `Sas`
/// or `Qr` object.
#[wasm_bindgen(js_name = "getVerification")]
pub fn get_verification(
&self,
user_id: &identifiers::UserId,
flow_id: &str,
) -> Result<JsValue, JsError> {
self.inner
.get_verification(&user_id.inner, flow_id)
.map(verification::Verification)
.map(JsValue::try_from)
.transpose()
.map(JsValue::from)
}
/// Get a verification request object with the given flow ID.
#[wasm_bindgen(js_name = "getVerificationRequest")]
pub fn get_verification_request(
&self,
user_id: &identifiers::UserId,
flow_id: &str,
) -> Option<verification::VerificationRequest> {
self.inner.get_verification_request(&user_id.inner, flow_id).map(Into::into)
}
/// Get all the verification requests of a given user.
#[wasm_bindgen(js_name = "getVerificationRequests")]
pub fn get_verification_requests(&self, user_id: &identifiers::UserId) -> Array {
self.inner
.get_verification_requests(&user_id.inner)
.into_iter()
.map(verification::VerificationRequest::from)
.map(JsValue::from)
.collect()
}
/// Receive a verification event.
///
/// This method can be used to pass verification events that are happening
/// in rooms to the `OlmMachine`. The event should be in the decrypted form.
#[wasm_bindgen(js_name = "receiveVerificationEvent")]
pub fn receive_verification_event(
&self,
event: &str,
room_id: &identifiers::RoomId,
) -> Result<Promise, JsError> {
let room_id = room_id.inner.clone();
let event: ruma::events::AnySyncMessageLikeEvent = serde_json::from_str(event)?;
let event = event.into_full_event(room_id);
let me = self.inner.clone();
Ok(future_to_promise(async move {
Ok(me.receive_verification_event(&event).await.map(|_| JsValue::UNDEFINED)?)
}))
}
/// Export the keys that match the given predicate.
///
/// `predicate` is a closure that will be called for every known
/// `InboundGroupSession`, which represents a room key. If the closure
/// returns `true`, the `InboundGroupSession` will be included in the
/// export, otherwise it won't.
#[wasm_bindgen(js_name = "exportRoomKeys")]
pub fn export_room_keys(&self, predicate: Function) -> Promise {
let me = self.inner.clone();
future_to_promise(async move {
Ok(serde_json::to_string(
&me.export_room_keys(|session| {
let session = session.clone();
predicate
.call1(&JsValue::NULL, &olm::InboundGroupSession::from(session).into())
.expect("Predicate function passed to `export_room_keys` failed")
.as_bool()
.unwrap_or(false)
})
.await?,
)?)
})
}
/// Import the given room keys into our store.
///
/// `exported_keys` is a list of previously exported keys that should be
/// imported into our store. If we already have a better version of a key,
/// the key will _not_ be imported.
///
/// `progress_listener` is a closure that takes 2 arguments: `progress` and
/// `total`, and returns nothing.
#[wasm_bindgen(js_name = "importRoomKeys")]
pub fn import_room_keys(
&self,
exported_room_keys: &str,
progress_listener: Function,
) -> Result<Promise, JsError> {
let me = self.inner.clone();
let exported_room_keys: Vec<matrix_sdk_crypto::olm::ExportedRoomKey> =
serde_json::from_str(exported_room_keys)?;
Ok(future_to_promise(async move {
let matrix_sdk_crypto::RoomKeyImportResult { imported_count, total_count, keys } = me
.import_room_keys(exported_room_keys, false, |progress, total| {
let progress: u64 = progress.try_into().unwrap();
let total: u64 = total.try_into().unwrap();
progress_listener
.call2(&JsValue::NULL, &JsValue::from(progress), &JsValue::from(total))
.expect("Progress listener passed to `import_room_keys` failed");
})
.await?;
Ok(serde_json::to_string(&json!({
"imported_count": imported_count,
"total_count": total_count,
"keys": keys,
}))?)
}))
}
/// Encrypt the list of exported room keys using the given passphrase.
///
/// `exported_room_keys` is a list of sessions that should be encrypted
/// (it's generally returned by `export_room_keys`). `passphrase` is the
/// passphrase that will be used to encrypt the exported room keys. And
/// `rounds` is the number of rounds that should be used for the key
/// derivation when the passphrase gets turned into an AES key. More rounds
/// are increasingly computationnally intensive and as such help against
/// brute-force attacks. Should be at least `10_000`, while values in the
/// `100_000` ranges should be preferred.
#[wasm_bindgen(js_name = "encryptExportedRoomKeys")]
pub fn encrypt_exported_room_keys(
exported_room_keys: &str,
passphrase: &str,
rounds: u32,
) -> Result<String, JsError> {
let exported_room_keys: Vec<matrix_sdk_crypto::olm::ExportedRoomKey> =
serde_json::from_str(exported_room_keys)?;
Ok(matrix_sdk_crypto::encrypt_room_key_export(&exported_room_keys, passphrase, rounds)?)
}
/// Try to decrypt a reader into a list of exported room keys.
///
/// `encrypted_exported_room_keys` is the result from
/// `encrypt_exported_room_keys`. `passphrase` is the passphrase that was
/// used when calling `encrypt_exported_room_keys`.
#[wasm_bindgen(js_name = "decryptExportedRoomKeys")]
pub fn decrypt_exported_room_keys(
encrypted_exported_room_keys: &str,
passphrase: &str,
) -> Result<String, JsError> {
Ok(serde_json::to_string(&matrix_sdk_crypto::decrypt_room_key_export(
encrypted_exported_room_keys.as_bytes(),
passphrase,
)?)?)
}
/// Register a callback which will be called whenever there is an update to
/// a room key.
///
/// `callback` should be a function that takes a single argument (an array
/// of {@link RoomKeyInfo}) and returns a Promise.
#[wasm_bindgen(js_name = "registerRoomKeyUpdatedCallback")]
pub async fn register_room_key_updated_callback(&self, callback: Function) {
let stream = self.inner.store().room_keys_received_stream();
// fire up a promise chain which will call `cb` on each result from the stream
spawn_local(async move {
// take a reference to `callback` (which we then pass into the closure), to stop
// the callback being moved into the closure (which would mean we could only
// call the closure once)
let callback_ref = &callback;
stream.for_each(move |item| send_room_key_info_to_callback(callback_ref, item)).await;
});
}
/// Shut down the `OlmMachine`.
///
/// The `OlmMachine` cannot be used after this method has been called.
///
/// All associated resources will be closed too, like IndexedDB
/// connections.
pub fn close(self) {}
}
// helper for register_room_key_received_callback: wraps the key info
// into our own RoomKeyInfo struct, and passes it into the javascript
// function
async fn send_room_key_info_to_callback(
callback: &Function,
room_key_info: Vec<matrix_sdk_crypto::store::RoomKeyInfo>,
) {
let rki: Array = room_key_info.into_iter().map(RoomKeyInfo::from).map(JsValue::from).collect();
match promise_result_to_future(callback.call1(&JsValue::NULL, &rki)).await {
Ok(_) => (),
Err(e) => {
warn!("Error calling room-key-received callback: {:?}", e);
}
}
}
/// Given a result from a javascript function which returns a Promise (or throws
/// an exception before returning one), convert the result to a rust Future
/// which completes with the result of the promise
async fn promise_result_to_future(res: Result<JsValue, JsValue>) -> Result<JsValue, JsValue> {
match res {
Ok(retval) => {
if !retval.has_type::<Promise>() {
panic!("not a promise");
}
let prom: Promise = retval.dyn_into().map_err(|v| {
JsError::new(&format!("function returned a non-Promise value {v:?}"))
})?;
JsFuture::from(prom).await
}
Err(e) => {
// the function threw an exception before it returned the promise. We can just
// return the error as an error result.
Err(e)
}
}
}
@@ -0,0 +1,33 @@
/// We have the following pattern quite often in our code:
///
/// ```rust
/// struct Foo {
/// inner: Bar,
/// }
///
/// impl From<Bar> for Foo {
/// fn from(inner: Bar) -> Self {
/// Self { inner }
/// }
/// }
/// ```
///
/// Because I feel lazy, let's do a macro to write this:
///
/// ```rust
/// struct Foo {
/// inner: Bar,
/// }
///
/// impl_from_to_inner!(Bar => Foo);
/// ```
#[macro_export]
macro_rules! impl_from_to_inner {
($from:ty => $to:ty) => {
impl From<$from> for $to {
fn from(inner: $from) -> Self {
Self { inner }
}
}
};
}
+79
View File
@@ -0,0 +1,79 @@
//! Olm types.
use wasm_bindgen::prelude::*;
use crate::{identifiers, impl_from_to_inner, vodozemac::Curve25519PublicKey};
/// Struct representing the state of our private cross signing keys,
/// it shows which private cross signing keys we have locally stored.
#[wasm_bindgen]
#[derive(Debug)]
pub struct CrossSigningStatus {
inner: matrix_sdk_crypto::olm::CrossSigningStatus,
}
impl_from_to_inner!(matrix_sdk_crypto::olm::CrossSigningStatus => CrossSigningStatus);
#[wasm_bindgen]
impl CrossSigningStatus {
/// Do we have the master key?
#[wasm_bindgen(getter, js_name = "hasMaster")]
pub fn has_master(&self) -> bool {
self.inner.has_master
}
/// Do we have the self signing key? This one is necessary to sign
/// our own devices.
#[wasm_bindgen(getter, js_name = "hasSelfSigning")]
pub fn has_self_signing(&self) -> bool {
self.inner.has_self_signing
}
/// Do we have the user signing key? This one is necessary to sign
/// other users.
#[wasm_bindgen(getter, js_name = "hasUserSigning")]
pub fn has_user_signing(&self) -> bool {
self.inner.has_user_signing
}
}
/// Inbound group session.
///
/// Inbound group sessions are used to exchange room messages between a group of
/// participants. Inbound group sessions are used to decrypt the room messages.
#[wasm_bindgen]
#[derive(Debug)]
pub struct InboundGroupSession {
inner: matrix_sdk_crypto::olm::InboundGroupSession,
}
impl_from_to_inner!(matrix_sdk_crypto::olm::InboundGroupSession => InboundGroupSession);
#[wasm_bindgen]
impl InboundGroupSession {
/// The room where this session is used in.
#[wasm_bindgen(getter, js_name = "roomId")]
pub fn room_id(&self) -> identifiers::RoomId {
self.inner.room_id().to_owned().into()
}
/// The Curve25519 key of the sender of this session, as a
/// [Curve25519PublicKey].
#[wasm_bindgen(getter, js_name = "senderKey")]
pub fn sender_key(&self) -> Curve25519PublicKey {
self.inner.sender_key().into()
}
/// Returns the unique identifier for this session.
#[wasm_bindgen(getter, js_name = "sessionId")]
pub fn session_id(&self) -> String {
self.inner.session_id().to_owned()
}
/// Has the session been imported from a file or server-side backup? As
/// opposed to being directly received as an `m.room_key` event.
#[wasm_bindgen(js_name = "hasBeenImported")]
pub fn has_been_imported(&self) -> bool {
self.inner.has_been_imported()
}
}
@@ -0,0 +1,485 @@
//! Types to handle requests.
use js_sys::JsString;
use matrix_sdk_crypto::{
requests::{
KeysBackupRequest as OriginalKeysBackupRequest,
KeysQueryRequest as OriginalKeysQueryRequest,
RoomMessageRequest as OriginalRoomMessageRequest,
ToDeviceRequest as OriginalToDeviceRequest,
UploadSigningKeysRequest as OriginalUploadSigningKeysRequest,
},
OutgoingRequests,
};
use ruma::{
api::client::keys::{
claim_keys::v3::Request as OriginalKeysClaimRequest,
upload_keys::v3::Request as OriginalKeysUploadRequest,
upload_signatures::v3::Request as OriginalSignatureUploadRequest,
},
events::EventContent,
};
use wasm_bindgen::prelude::*;
/** Outgoing Requests * */
/// Data for a request to the `/keys/upload` API endpoint
/// ([specification]).
///
/// Publishes end-to-end encryption keys for the device.
///
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#post_matrixclientv3keysupload
#[derive(Debug)]
#[wasm_bindgen(getter_with_clone)]
pub struct KeysUploadRequest {
/// The request ID.
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded string containing the rest of the payload: `device_keys`,
/// `one_time_keys`, `fallback_keys`.
///
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
#[wasm_bindgen]
impl KeysUploadRequest {
/// Create a new `KeysUploadRequest`.
#[wasm_bindgen(constructor)]
pub fn new(id: JsString, body: JsString) -> KeysUploadRequest {
Self { id: Some(id), body }
}
/// Get its request type.
#[wasm_bindgen(getter, js_name = "type")]
pub fn request_type(&self) -> RequestType {
RequestType::KeysUpload
}
}
/// Data for a request to the `/keys/query` API endpoint
/// ([specification]).
///
/// Returns the current devices and identity keys for the given users.
///
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#post_matrixclientv3keysquery
#[derive(Debug)]
#[wasm_bindgen(getter_with_clone)]
pub struct KeysQueryRequest {
/// The request ID.
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded string containing the rest of the payload: `timeout`,
/// `device_keys`, `token`.
///
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
#[wasm_bindgen]
impl KeysQueryRequest {
/// Create a new `KeysQueryRequest`.
#[wasm_bindgen(constructor)]
pub fn new(id: JsString, body: JsString) -> KeysQueryRequest {
Self { id: Some(id), body }
}
/// Get its request type.
#[wasm_bindgen(getter, js_name = "type")]
pub fn request_type(&self) -> RequestType {
RequestType::KeysQuery
}
}
/// Data for a request to the `/keys/claim` API endpoint
/// ([specification]).
///
/// Claims one-time keys that can be used to establish 1-to-1 E2EE
/// sessions.
///
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#post_matrixclientv3keysclaim
#[derive(Debug)]
#[wasm_bindgen(getter_with_clone)]
pub struct KeysClaimRequest {
/// The request ID.
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded string containing the rest of the payload: `timeout`,
/// `one_time_keys`.
///
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
#[wasm_bindgen]
impl KeysClaimRequest {
/// Create a new `KeysClaimRequest`.
#[wasm_bindgen(constructor)]
pub fn new(id: JsString, body: JsString) -> KeysClaimRequest {
Self { id: Some(id), body }
}
/// Get its request type.
#[wasm_bindgen(getter, js_name = "type")]
pub fn request_type(&self) -> RequestType {
RequestType::KeysClaim
}
}
/// Data for a request to the `/sendToDevice` API endpoint
/// ([specification]).
///
/// Send an event to a single device or to a group of devices.
///
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#put_matrixclientv3sendtodeviceeventtypetxnid
#[derive(Debug)]
#[wasm_bindgen(getter_with_clone)]
pub struct ToDeviceRequest {
/// The request ID.
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A string representing the type of event being sent to each devices.
#[wasm_bindgen(readonly)]
pub event_type: JsString,
/// A string representing a request identifier unique to the access token
/// used to send the request.
#[wasm_bindgen(readonly)]
pub txn_id: JsString,
/// A JSON-encoded string containing the rest of the payload: `messages`.
///
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
#[wasm_bindgen]
impl ToDeviceRequest {
/// Create a new `ToDeviceRequest`.
#[wasm_bindgen(constructor)]
pub fn new(
id: JsString,
event_type: JsString,
txn_id: JsString,
body: JsString,
) -> ToDeviceRequest {
Self { id: Some(id), event_type, txn_id, body }
}
/// Get its request type.
#[wasm_bindgen(getter, js_name = "type")]
pub fn request_type(&self) -> RequestType {
RequestType::ToDevice
}
}
/// Data for a request to the `/keys/signatures/upload` API endpoint
/// ([specification]).
///
/// Publishes cross-signing signatures for the user.
///
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#post_matrixclientv3keyssignaturesupload
#[derive(Debug)]
#[wasm_bindgen(getter_with_clone)]
pub struct SignatureUploadRequest {
/// The request ID.
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded string containing the rest of the payload: `signed_keys`.
///
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
#[wasm_bindgen]
impl SignatureUploadRequest {
/// Create a new `SignatureUploadRequest`.
#[wasm_bindgen(constructor)]
pub fn new(id: JsString, body: JsString) -> SignatureUploadRequest {
Self { id: Some(id), body }
}
/// Get its request type.
#[wasm_bindgen(getter, js_name = "type")]
pub fn request_type(&self) -> RequestType {
RequestType::SignatureUpload
}
}
/// A customized owned request type for sending out room messages
/// ([specification]).
///
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
#[derive(Debug)]
#[wasm_bindgen(getter_with_clone)]
pub struct RoomMessageRequest {
/// The request ID.
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A string representing the room to send the event to.
#[wasm_bindgen(readonly)]
pub room_id: JsString,
/// A string representing the transaction ID for this event.
///
/// Clients should generate an ID unique across requests with the same
/// access token; it will be used by the server to ensure idempotency of
/// requests.
#[wasm_bindgen(readonly)]
pub txn_id: JsString,
/// A string representing the type of event to be sent.
#[wasm_bindgen(readonly)]
pub event_type: JsString,
/// A JSON-encoded string containing the message's content.
#[wasm_bindgen(readonly, js_name = "body")]
pub content: JsString,
}
#[wasm_bindgen]
impl RoomMessageRequest {
/// Create a new `RoomMessageRequest`.
#[wasm_bindgen(constructor)]
pub fn new(
id: JsString,
room_id: JsString,
txn_id: JsString,
event_type: JsString,
content: JsString,
) -> RoomMessageRequest {
Self { id: Some(id), room_id, txn_id, event_type, content }
}
/// Get its request type.
#[wasm_bindgen(getter, js_name = "type")]
pub fn request_type(&self) -> RequestType {
RequestType::RoomMessage
}
}
/// A request that will back up a batch of room keys to the server
/// ([specification]).
///
/// [specification]: https://spec.matrix.org/unstable/client-server-api/#put_matrixclientv3room_keyskeys
#[derive(Debug)]
#[wasm_bindgen(getter_with_clone)]
pub struct KeysBackupRequest {
/// The request ID.
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded string containing the rest of the payload: `rooms`.
///
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
#[wasm_bindgen]
impl KeysBackupRequest {
/// Create a new `KeysBackupRequest`.
#[wasm_bindgen(constructor)]
pub fn new(id: JsString, body: JsString) -> KeysBackupRequest {
Self { id: Some(id), body }
}
/// Get its request type.
#[wasm_bindgen(getter, js_name = "type")]
pub fn request_type(&self) -> RequestType {
RequestType::KeysBackup
}
}
/** Other Requests * */
/// Request that will publish a cross signing identity.
///
/// This uploads the public cross signing key triplet.
#[wasm_bindgen(getter_with_clone)]
#[derive(Debug)]
pub struct SigningKeysUploadRequest {
/// The request ID.
#[wasm_bindgen(readonly)]
pub id: Option<JsString>,
/// A JSON-encoded string containing the rest of the payload: `master_key`,
/// `self_signing_key`, `user_signing_key`.
///
/// It represents the body of the HTTP request.
#[wasm_bindgen(readonly)]
pub body: JsString,
}
macro_rules! request {
(
$destination_request:ident from $source_request:ident
$( extracts $( $field_name:ident : $field_type:tt ),+ $(,)? )?
$( $( and )? groups $( $grouped_field_name:ident ),+ $(,)? )?
) => {
impl TryFrom<&$source_request> for $destination_request {
type Error = serde_json::Error;
fn try_from(request: &$source_request) -> Result<Self, Self::Error> {
request!(
@__try_from $destination_request from $source_request
(request_id = None, request = request)
$( extracts [ $( $field_name : $field_type, )+ ] )?
$( groups [ $( $grouped_field_name, )+ ] )?
)
}
}
impl TryFrom<(String, &$source_request)> for $destination_request {
type Error = serde_json::Error;
fn try_from(
(request_id, request): (String, &$source_request),
) -> Result<Self, Self::Error> {
request!(
@__try_from $destination_request from $source_request
(request_id = Some(request_id.into()), request = request)
$( extracts [ $( $field_name : $field_type, )+ ] )?
$( groups [ $( $grouped_field_name, )+ ] )?
)
}
}
};
(
@__try_from $destination_request:ident from $source_request:ident
(request_id = $request_id:expr, request = $request:expr)
$( extracts [ $( $field_name:ident : $field_type:tt ),* $(,)? ] )?
$( groups [ $( $grouped_field_name:ident ),* $(,)? ] )?
) => {
{
Ok($destination_request {
id: $request_id,
$(
$(
$field_name: request!(@__field $field_name : $field_type ; request = $request),
)*
)?
$(
body: {
let mut map = serde_json::Map::new();
$(
map.insert(stringify!($grouped_field_name).to_owned(), serde_json::to_value(&$request.$grouped_field_name).unwrap());
)*
let object = serde_json::Value::Object(map);
serde_json::to_string(&object)?.into()
}
)?
})
}
};
( @__field $field_name:ident : $field_type:ident ; request = $request:expr ) => {
request!(@__field_type as $field_type ; request = $request, field_name = $field_name)
};
( @__field_type as string ; request = $request:expr, field_name = $field_name:ident ) => {
$request.$field_name.to_string().into()
};
( @__field_type as json ; request = $request:expr, field_name = $field_name:ident ) => {
serde_json::to_string(&$request.$field_name)?.into()
};
( @__field_type as event_type ; request = $request:expr, field_name = $field_name:ident ) => {
$request.content.event_type().to_string().into()
};
}
// Outgoing Requests
request!(KeysUploadRequest from OriginalKeysUploadRequest groups device_keys, one_time_keys, fallback_keys);
request!(KeysQueryRequest from OriginalKeysQueryRequest groups timeout, device_keys, token);
request!(KeysClaimRequest from OriginalKeysClaimRequest groups timeout, one_time_keys);
request!(ToDeviceRequest from OriginalToDeviceRequest extracts event_type: string, txn_id: string and groups messages);
request!(SignatureUploadRequest from OriginalSignatureUploadRequest groups signed_keys);
request!(RoomMessageRequest from OriginalRoomMessageRequest extracts room_id: string, txn_id: string, event_type: event_type, content: json);
request!(KeysBackupRequest from OriginalKeysBackupRequest groups rooms);
// Other Requests
request!(SigningKeysUploadRequest from OriginalUploadSigningKeysRequest groups master_key, self_signing_key, user_signing_key);
// JavaScript has no complex enums like Rust. To return structs of
// different types, we have no choice that hiding everything behind a
// `JsValue`.
pub(crate) struct OutgoingRequest(pub(crate) matrix_sdk_crypto::OutgoingRequest);
impl TryFrom<OutgoingRequest> for JsValue {
type Error = serde_json::Error;
fn try_from(outgoing_request: OutgoingRequest) -> Result<Self, Self::Error> {
let request_id = outgoing_request.0.request_id().to_string();
Ok(match outgoing_request.0.request() {
OutgoingRequests::KeysUpload(request) => {
JsValue::from(KeysUploadRequest::try_from((request_id, request))?)
}
OutgoingRequests::KeysQuery(request) => {
JsValue::from(KeysQueryRequest::try_from((request_id, request))?)
}
OutgoingRequests::KeysClaim(request) => {
JsValue::from(KeysClaimRequest::try_from((request_id, request))?)
}
OutgoingRequests::ToDeviceRequest(request) => {
JsValue::from(ToDeviceRequest::try_from((request_id, request))?)
}
OutgoingRequests::SignatureUpload(request) => {
JsValue::from(SignatureUploadRequest::try_from((request_id, request))?)
}
OutgoingRequests::RoomMessage(request) => {
JsValue::from(RoomMessageRequest::try_from((request_id, request))?)
}
OutgoingRequests::KeysBackup(request) => {
JsValue::from(KeysBackupRequest::try_from((request_id, request))?)
}
})
}
}
/// Represent the type of a request.
#[wasm_bindgen]
#[derive(Debug)]
pub enum RequestType {
/// Represents a `KeysUploadRequest`.
KeysUpload,
/// Represents a `KeysQueryRequest`.
KeysQuery,
/// Represents a `KeysClaimRequest`.
KeysClaim,
/// Represents a `ToDeviceRequest`.
ToDevice,
/// Represents a `SignatureUploadRequest`.
SignatureUpload,
/// Represents a `RoomMessageRequest`.
RoomMessage,
/// Represents a `KeysBackupRequest`.
KeysBackup,
}
@@ -0,0 +1,210 @@
//! Types related to responses.
use js_sys::{Array, JsString};
use matrix_sdk_common::deserialized_responses::{AlgorithmInfo, EncryptionInfo};
use matrix_sdk_crypto::IncomingResponse;
pub(crate) use ruma::api::client::{
backup::add_backup_keys::v3::Response as KeysBackupResponse,
keys::{
claim_keys::v3::Response as KeysClaimResponse, get_keys::v3::Response as KeysQueryResponse,
upload_keys::v3::Response as KeysUploadResponse,
upload_signatures::v3::Response as SignatureUploadResponse,
},
message::send_message_event::v3::Response as RoomMessageResponse,
to_device::send_event_to_device::v3::Response as ToDeviceResponse,
};
use ruma::api::IncomingResponse as RumaIncomingResponse;
use wasm_bindgen::prelude::*;
use crate::{encryption, identifiers, requests::RequestType};
pub(crate) fn response_from_string(body: &str) -> http::Result<http::Response<Vec<u8>>> {
http::Response::builder().status(200).body(body.as_bytes().to_vec())
}
/// Intermediate private type to store an incoming owned response,
/// without the need to manage lifetime.
pub(crate) enum OwnedResponse {
KeysUpload(KeysUploadResponse),
KeysQuery(KeysQueryResponse),
KeysClaim(KeysClaimResponse),
ToDevice(ToDeviceResponse),
SignatureUpload(SignatureUploadResponse),
RoomMessage(RoomMessageResponse),
KeysBackup(KeysBackupResponse),
}
impl From<KeysUploadResponse> for OwnedResponse {
fn from(response: KeysUploadResponse) -> Self {
OwnedResponse::KeysUpload(response)
}
}
impl From<KeysQueryResponse> for OwnedResponse {
fn from(response: KeysQueryResponse) -> Self {
OwnedResponse::KeysQuery(response)
}
}
impl From<KeysClaimResponse> for OwnedResponse {
fn from(response: KeysClaimResponse) -> Self {
OwnedResponse::KeysClaim(response)
}
}
impl From<ToDeviceResponse> for OwnedResponse {
fn from(response: ToDeviceResponse) -> Self {
OwnedResponse::ToDevice(response)
}
}
impl From<SignatureUploadResponse> for OwnedResponse {
fn from(response: SignatureUploadResponse) -> Self {
Self::SignatureUpload(response)
}
}
impl From<RoomMessageResponse> for OwnedResponse {
fn from(response: RoomMessageResponse) -> Self {
OwnedResponse::RoomMessage(response)
}
}
impl From<KeysBackupResponse> for OwnedResponse {
fn from(r: KeysBackupResponse) -> Self {
Self::KeysBackup(r)
}
}
impl TryFrom<(RequestType, http::Response<Vec<u8>>)> for OwnedResponse {
type Error = JsError;
fn try_from(
(request_type, response): (RequestType, http::Response<Vec<u8>>),
) -> Result<Self, Self::Error> {
match request_type {
RequestType::KeysUpload => {
KeysUploadResponse::try_from_http_response(response).map(Into::into)
}
RequestType::KeysQuery => {
KeysQueryResponse::try_from_http_response(response).map(Into::into)
}
RequestType::KeysClaim => {
KeysClaimResponse::try_from_http_response(response).map(Into::into)
}
RequestType::ToDevice => {
ToDeviceResponse::try_from_http_response(response).map(Into::into)
}
RequestType::SignatureUpload => {
SignatureUploadResponse::try_from_http_response(response).map(Into::into)
}
RequestType::RoomMessage => {
RoomMessageResponse::try_from_http_response(response).map(Into::into)
}
RequestType::KeysBackup => {
KeysBackupResponse::try_from_http_response(response).map(Into::into)
}
}
.map_err(JsError::from)
}
}
impl<'a> From<&'a OwnedResponse> for IncomingResponse<'a> {
fn from(response: &'a OwnedResponse) -> Self {
match response {
OwnedResponse::KeysUpload(response) => IncomingResponse::KeysUpload(response),
OwnedResponse::KeysQuery(response) => IncomingResponse::KeysQuery(response),
OwnedResponse::KeysClaim(response) => IncomingResponse::KeysClaim(response),
OwnedResponse::ToDevice(response) => IncomingResponse::ToDevice(response),
OwnedResponse::SignatureUpload(response) => IncomingResponse::SignatureUpload(response),
OwnedResponse::RoomMessage(response) => IncomingResponse::RoomMessage(response),
OwnedResponse::KeysBackup(response) => IncomingResponse::KeysBackup(response),
}
}
}
/// A decrypted room event.
#[wasm_bindgen(getter_with_clone)]
#[derive(Debug)]
pub struct DecryptedRoomEvent {
/// The JSON-encoded decrypted event.
#[wasm_bindgen(readonly)]
pub event: JsString,
encryption_info: Option<EncryptionInfo>,
}
#[wasm_bindgen]
impl DecryptedRoomEvent {
/// The user ID of the event sender, note this is untrusted data
/// unless the `verification_state` is as well trusted.
#[wasm_bindgen(getter)]
pub fn sender(&self) -> Option<identifiers::UserId> {
Some(identifiers::UserId::from(self.encryption_info.as_ref()?.sender.clone()))
}
/// The device ID of the device that sent us the event, note this
/// is untrusted data unless `verification_state` is as well
/// trusted.
#[wasm_bindgen(getter, js_name = "senderDevice")]
pub fn sender_device(&self) -> Option<identifiers::DeviceId> {
Some(self.encryption_info.as_ref()?.sender_device.as_ref()?.clone().into())
}
/// The Curve25519 key of the device that created the megolm
/// decryption key originally.
#[wasm_bindgen(getter, js_name = "senderCurve25519Key")]
pub fn sender_curve25519_key(&self) -> Option<JsString> {
Some(match &self.encryption_info.as_ref()?.algorithm_info {
AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, .. } => curve25519_key.clone().into(),
})
}
/// The signing Ed25519 key that have created the megolm key that
/// was used to decrypt this session.
#[wasm_bindgen(getter, js_name = "senderClaimedEd25519Key")]
pub fn sender_claimed_ed25519_key(&self) -> Option<JsString> {
match &self.encryption_info.as_ref()?.algorithm_info {
AlgorithmInfo::MegolmV1AesSha2 { sender_claimed_keys, .. } => {
sender_claimed_keys.get(&ruma::DeviceKeyAlgorithm::Ed25519).cloned().map(Into::into)
}
}
}
/// Chain of Curve25519 keys through which this session was
/// forwarded, via `m.forwarded_room_key` events.
#[wasm_bindgen(getter, js_name = "forwardingCurve25519KeyChain")]
pub fn forwarding_curve25519_key_chain(&self) -> Array {
Array::new()
}
/// The verification state of the device that sent us the event,
/// note this is the state of the device at the time of
/// decryption. It may change in the future if a device gets
/// verified or deleted.
#[wasm_bindgen(js_name = "shieldState")]
pub fn shield_state(&self, strict: bool) -> Option<encryption::ShieldState> {
let state = &self.encryption_info.as_ref()?.verification_state;
if strict {
Some(state.to_shield_state_strict().into())
} else {
Some(state.to_shield_state_lax().into())
}
}
}
impl From<matrix_sdk_common::deserialized_responses::TimelineEvent> for DecryptedRoomEvent {
fn from(value: matrix_sdk_common::deserialized_responses::TimelineEvent) -> Self {
Self {
event: value.event.json().get().to_owned().into(),
encryption_info: value.encryption_info,
}
}
}
@@ -0,0 +1,76 @@
//! Store types.
use wasm_bindgen::prelude::*;
use crate::{
encryption::EncryptionAlgorithm, identifiers::RoomId, impl_from_to_inner,
vodozemac::Curve25519PublicKey,
};
/// A struct containing private cross signing keys that can be backed
/// up or uploaded to the secret store.
#[wasm_bindgen]
#[derive(Debug)]
pub struct CrossSigningKeyExport {
pub(crate) inner: matrix_sdk_crypto::store::CrossSigningKeyExport,
}
impl_from_to_inner!(matrix_sdk_crypto::store::CrossSigningKeyExport => CrossSigningKeyExport);
#[wasm_bindgen]
impl CrossSigningKeyExport {
/// The seed of the master key encoded as unpadded base64.
#[wasm_bindgen(getter, js_name = "masterKey")]
pub fn master_key(&self) -> Option<String> {
self.inner.master_key.clone()
}
/// The seed of the self signing key encoded as unpadded base64.
#[wasm_bindgen(getter, js_name = "self_signing_key")]
pub fn self_signing_key(&self) -> Option<String> {
self.inner.self_signing_key.clone()
}
/// The seed of the user signing key encoded as unpadded base64.
#[wasm_bindgen(getter, js_name = "userSigningKey")]
pub fn user_signing_key(&self) -> Option<String> {
self.inner.user_signing_key.clone()
}
}
/// Information on a room key that has been received or imported.
#[wasm_bindgen]
#[derive(Debug)]
pub struct RoomKeyInfo {
pub(crate) inner: matrix_sdk_crypto::store::RoomKeyInfo,
}
impl_from_to_inner!(matrix_sdk_crypto::store::RoomKeyInfo => RoomKeyInfo);
#[wasm_bindgen]
impl RoomKeyInfo {
/// The {@link EncryptionAlgorithm} that this key is used for. Will be one
/// of the `m.megolm.*` algorithms.
#[wasm_bindgen(getter)]
pub fn algorithm(&self) -> EncryptionAlgorithm {
self.inner.algorithm.clone().into()
}
/// The room where the key is used.
#[wasm_bindgen(getter, js_name = "roomId")]
pub fn room_id(&self) -> RoomId {
self.inner.room_id.clone().into()
}
/// The Curve25519 key of the device which initiated the session originally.
#[wasm_bindgen(getter, js_name = "senderKey")]
pub fn sender_key(&self) -> Curve25519PublicKey {
self.inner.sender_key.into()
}
/// The ID of the session that the key is for.
#[wasm_bindgen(getter, js_name = "sessionId")]
pub fn session_id(&self) -> String {
self.inner.session_id.clone()
}
}
@@ -0,0 +1,69 @@
//! `GET /_matrix/client/*/sync`
use js_sys::Array;
use wasm_bindgen::prelude::*;
use crate::{identifiers, js::downcast};
/// Information on E2E device updates.
#[wasm_bindgen]
#[derive(Debug)]
pub struct DeviceLists {
pub(crate) inner: ruma::api::client::sync::sync_events::DeviceLists,
}
#[wasm_bindgen]
impl DeviceLists {
/// Create an empty `DeviceLists`.
///
/// `changed` and `left` must be an array of `UserId`.
#[wasm_bindgen(constructor)]
pub fn new(changed: Option<Array>, left: Option<Array>) -> Result<DeviceLists, JsError> {
let mut inner = ruma::api::client::sync::sync_events::DeviceLists::default();
inner.changed = changed
.unwrap_or_default()
.iter()
.map(|user| Ok(downcast::<identifiers::UserId>(&user, "UserId")?.inner.clone()))
.collect::<Result<Vec<ruma::OwnedUserId>, JsError>>()?;
inner.left = left
.unwrap_or_default()
.iter()
.map(|user| Ok(downcast::<identifiers::UserId>(&user, "UserId")?.inner.clone()))
.collect::<Result<Vec<ruma::OwnedUserId>, JsError>>()?;
Ok(Self { inner })
}
/// Returns true if there are no device list updates.
#[wasm_bindgen(js_name = "isEmpty")]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
/// List of users who have updated their device identity keys or
/// who now share an encrypted room with the client since the
/// previous sync
#[wasm_bindgen(getter)]
pub fn changed(&self) -> Array {
self.inner
.changed
.iter()
.map(|user| identifiers::UserId::from(user.clone()))
.map(JsValue::from)
.collect()
}
/// List of users who no longer share encrypted rooms since the
/// previous sync response.
#[wasm_bindgen(getter)]
pub fn left(&self) -> Array {
self.inner
.left
.iter()
.map(|user| identifiers::UserId::from(user.clone()))
.map(JsValue::from)
.collect()
}
}
@@ -0,0 +1,287 @@
use wasm_bindgen::prelude::*;
/// Logger level.
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub enum LoggerLevel {
/// `TRACE` level.
///
/// Designate very low priority, often extremely verbose,
/// information.
Trace,
/// `DEBUG` level.
///
/// Designate lower priority information.
Debug,
/// `INFO` level.
///
/// Designate useful information.
Info,
/// `WARN` level.
///
/// Designate hazardous situations.
Warn,
/// `ERROR` level.
///
/// Designate very serious errors.
Error,
}
#[cfg(feature = "tracing")]
mod inner {
use std::{
fmt,
fmt::Write as _,
sync::{Arc, Once},
};
use tracing::{
field::{Field, Visit},
metadata::LevelFilter,
Event, Level, Metadata, Subscriber,
};
use tracing_subscriber::{
layer::{Context, Layer as TracingLayer},
prelude::*,
reload, Registry,
};
use super::*;
type TracingInner = Arc<reload::Handle<Layer, Registry>>;
/// Type to install and to manipulate the tracing layer.
#[wasm_bindgen]
pub struct Tracing {
handle: TracingInner,
}
impl fmt::Debug for Tracing {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Tracing").finish_non_exhaustive()
}
}
#[wasm_bindgen]
impl Tracing {
/// Check whether the `tracing` feature has been enabled.
#[wasm_bindgen(js_name = "isAvailable")]
pub fn is_available() -> bool {
true
}
/// Install the tracing layer.
///
/// `Tracing` is a singleton. Once it is installed,
/// consecutive calls to the constructor will construct a new
/// `Tracing` object but with the exact same inner
/// state. Calling the constructor with a new `min_level` will
/// just update the `min_level` parameter; in that regard, it
/// is similar to calling the `min_level` method on an
/// existing `Tracing` object.
#[wasm_bindgen(constructor)]
pub fn new(min_level: LoggerLevel) -> Result<Tracing, JsError> {
static mut INSTALL: Option<TracingInner> = None;
static INSTALLED: Once = Once::new();
INSTALLED.call_once(|| {
let (filter, reload_handle) = reload::Layer::new(Layer::new(min_level.clone()));
tracing_subscriber::registry().with(filter).init();
unsafe { INSTALL = Some(Arc::new(reload_handle)) };
});
let tracing = Tracing {
handle: unsafe { INSTALL.as_ref() }
.cloned()
.expect("`Tracing` has not been installed correctly"),
};
// If it's not the first call to `install`, the
// `min_level` can be different. Let's update it.
tracing.min_level(min_level);
Ok(tracing)
}
/// Re-define the minimum logger level.
#[wasm_bindgen(setter, js_name = "minLevel")]
pub fn min_level(&self, min_level: LoggerLevel) {
let _ = self.handle.modify(|layer| layer.min_level = min_level.into());
}
/// Turn the logger on, i.e. it emits logs again if it was turned
/// off.
#[wasm_bindgen(js_name = "turnOn")]
pub fn turn_on(&self) {
let _ = self.handle.modify(|layer| layer.turn_on());
}
/// Turn the logger off, i.e. it no long emits logs.
#[wasm_bindgen(js_name = "turnOff")]
pub fn turn_off(&self) {
let _ = self.handle.modify(|layer| layer.turn_off());
}
}
impl From<LoggerLevel> for Level {
fn from(value: LoggerLevel) -> Self {
use LoggerLevel::*;
match value {
Trace => Self::TRACE,
Debug => Self::DEBUG,
Info => Self::INFO,
Warn => Self::WARN,
Error => Self::ERROR,
}
}
}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console, js_name = "debug")]
fn log_debug(message: String);
#[wasm_bindgen(js_namespace = console, js_name = "info")]
fn log_info(message: String);
#[wasm_bindgen(js_namespace = console, js_name = "warn")]
fn log_warn(message: String);
#[wasm_bindgen(js_namespace = console, js_name = "error")]
fn log_error(message: String);
}
struct Layer {
min_level: Level,
enabled: bool,
}
impl Layer {
fn new<L>(min_level: L) -> Self
where
L: Into<Level>,
{
Self { min_level: min_level.into(), enabled: true }
}
fn turn_on(&mut self) {
self.enabled = true;
}
fn turn_off(&mut self) {
self.enabled = false;
}
}
impl<S> TracingLayer<S> for Layer
where
S: Subscriber,
{
fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool {
self.enabled && metadata.level() <= &self.min_level
}
fn max_level_hint(&self) -> Option<LevelFilter> {
if !self.enabled {
Some(LevelFilter::OFF)
} else {
Some(LevelFilter::from_level(self.min_level))
}
}
fn on_event(&self, event: &Event<'_>, _: Context<'_, S>) {
let mut recorder = StringVisitor::new();
event.record(&mut recorder);
let metadata = event.metadata();
let level = metadata.level();
let origin = metadata
.file()
.and_then(|file| metadata.line().map(|ln| format!("{file}:{ln}")))
.unwrap_or_default();
let message = format!("{level} {origin}{recorder}");
match *level {
Level::TRACE => log_debug(message),
Level::DEBUG => log_debug(message),
Level::INFO => log_info(message),
Level::WARN => log_warn(message),
Level::ERROR => log_error(message),
}
}
}
struct StringVisitor {
string: String,
}
impl StringVisitor {
fn new() -> Self {
Self { string: String::new() }
}
}
impl Visit for StringVisitor {
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
match field.name() {
"message" => {
if !self.string.is_empty() {
self.string.push('\n');
}
let _ = write!(self.string, "{value:?}");
}
field_name => {
let _ = write!(self.string, "\n{field_name} = {value:?}");
}
}
}
}
impl fmt::Display for StringVisitor {
fn fmt(&self, mut f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.string.is_empty() {
write!(&mut f, " {}", self.string)
} else {
Ok(())
}
}
}
}
#[cfg(not(feature = "tracing"))]
mod inner {
use super::*;
/// Type to install and to manipulate the tracing layer.
#[wasm_bindgen]
#[derive(Debug)]
pub struct Tracing;
#[wasm_bindgen]
impl Tracing {
/// Check whether the `tracing` feature has been enabled.
#[wasm_bindgen(js_name = "isAvailable")]
pub fn is_available() -> bool {
false
}
/// The `tracing` feature is not enabled, so this constructor
/// will raise an error.
#[wasm_bindgen(constructor)]
pub fn new(_min_level: LoggerLevel) -> Result<Tracing, JsError> {
Err(JsError::new("The `tracing` feature is disabled. Check `Tracing.isAvailable` before constructing `Tracing`"))
}
}
}
pub use inner::*;
+156
View File
@@ -0,0 +1,156 @@
//! Extra types, like `Signatures`.
use js_sys::Map;
use wasm_bindgen::prelude::*;
use crate::{
identifiers::{DeviceKeyId, UserId},
impl_from_to_inner,
vodozemac::Ed25519Signature,
};
/// A collection of `Signature`.
#[wasm_bindgen]
#[derive(Debug, Default)]
pub struct Signatures {
inner: matrix_sdk_crypto::types::Signatures,
}
impl_from_to_inner!(matrix_sdk_crypto::types::Signatures => Signatures);
#[wasm_bindgen]
impl Signatures {
/// Creates a new, empty, signatures collection.
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
matrix_sdk_crypto::types::Signatures::new().into()
}
/// Add the given signature from the given signer and the given key ID to
/// the collection.
#[wasm_bindgen(js_name = "addSignature")]
pub fn add_signature(
&mut self,
signer: &UserId,
key_id: &DeviceKeyId,
signature: &Ed25519Signature,
) -> Option<MaybeSignature> {
self.inner
.add_signature(signer.inner.clone(), key_id.inner.clone(), signature.inner)
.map(Into::into)
}
/// Try to find an Ed25519 signature from the given signer with
/// the given key ID.
#[wasm_bindgen(js_name = "getSignature")]
pub fn get_signature(&self, signer: &UserId, key_id: &DeviceKeyId) -> Option<Ed25519Signature> {
self.inner.get_signature(signer.inner.as_ref(), key_id.inner.as_ref()).map(Into::into)
}
/// Get the map of signatures that belong to the given user.
pub fn get(&self, signer: &UserId) -> Option<Map> {
let map = Map::new();
for (device_key_id, maybe_signature) in
self.inner.get(signer.inner.as_ref()).map(|map| {
map.iter().map(|(device_key_id, maybe_signature)| {
(
device_key_id.as_str().to_owned(),
MaybeSignature::from(maybe_signature.clone()),
)
})
})?
{
map.set(&device_key_id.into(), &maybe_signature.into());
}
Some(map)
}
/// Remove all the signatures we currently hold.
pub fn clear(&mut self) {
self.inner.clear();
}
/// Do we hold any signatures or is our collection completely
/// empty.
#[wasm_bindgen(js_name = "isEmpty")]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
/// How many signatures do we currently hold.
#[wasm_bindgen(getter)]
pub fn count(&self) -> usize {
self.inner.signature_count()
}
}
/// Represents a potentially decoded signature (but not a validated
/// one).
#[wasm_bindgen]
#[derive(Debug)]
pub struct Signature {
inner: matrix_sdk_crypto::types::Signature,
}
impl_from_to_inner!(matrix_sdk_crypto::types::Signature => Signature);
#[wasm_bindgen]
impl Signature {
/// Get the Ed25519 signature, if this is one.
#[wasm_bindgen(getter)]
pub fn ed25519(&self) -> Option<Ed25519Signature> {
self.inner.ed25519().map(Into::into)
}
/// Convert the signature to a base64 encoded string.
#[wasm_bindgen(js_name = "toBase64")]
pub fn to_base64(&self) -> String {
self.inner.to_base64()
}
}
type MaybeSignatureInner =
Result<matrix_sdk_crypto::types::Signature, matrix_sdk_crypto::types::InvalidSignature>;
/// Represents a signature that is either valid _or_ that could not be
/// decoded.
#[wasm_bindgen]
#[derive(Debug)]
pub struct MaybeSignature {
inner: MaybeSignatureInner,
}
impl_from_to_inner!(MaybeSignatureInner => MaybeSignature);
#[wasm_bindgen]
impl MaybeSignature {
/// Check whether the signature has been successfully decoded.
#[wasm_bindgen(js_name = "isValid")]
pub fn is_valid(&self) -> bool {
self.inner.is_ok()
}
/// Check whether the signature could not be successfully decoded.
#[wasm_bindgen(js_name = "isInvalid")]
pub fn is_invalid(&self) -> bool {
self.inner.is_err()
}
/// The signature, if successfully decoded.
#[wasm_bindgen(getter)]
pub fn signature(&self) -> Option<Signature> {
self.inner.as_ref().cloned().map(Into::into).ok()
}
/// The base64 encoded string that is claimed to contain a
/// signature but could not be decoded, if any.
#[wasm_bindgen(getter, js_name = "invalidSignatureSource")]
pub fn invalid_signature_source(&self) -> Option<String> {
match &self.inner {
Ok(_) => None,
Err(signature) => Some(signature.source.clone()),
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,193 @@
//! Vodozemac types.
use wasm_bindgen::prelude::*;
use crate::impl_from_to_inner;
/// An Ed25519 public key, used to verify digital signatures.
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct Ed25519PublicKey {
inner: vodozemac::Ed25519PublicKey,
}
#[wasm_bindgen]
impl Ed25519PublicKey {
/// The number of bytes an Ed25519 public key has.
#[wasm_bindgen(getter)]
pub fn length(&self) -> usize {
vodozemac::Ed25519PublicKey::LENGTH
}
/// Serialize an Ed25519 public key to an unpadded base64
/// representation.
#[wasm_bindgen(js_name = "toBase64")]
pub fn to_base64(&self) -> String {
self.inner.to_base64()
}
}
impl_from_to_inner!(vodozemac::Ed25519PublicKey => Ed25519PublicKey);
/// An Ed25519 digital signature, can be used to verify the
/// authenticity of a message.
#[wasm_bindgen]
#[derive(Debug)]
pub struct Ed25519Signature {
pub(crate) inner: vodozemac::Ed25519Signature,
}
impl_from_to_inner!(vodozemac::Ed25519Signature => Ed25519Signature);
#[wasm_bindgen]
impl Ed25519Signature {
/// Try to create an Ed25519 signature from an unpadded base64
/// representation.
#[wasm_bindgen(constructor)]
pub fn new(signature: String) -> Result<Ed25519Signature, JsError> {
Ok(Self { inner: vodozemac::Ed25519Signature::from_base64(signature.as_str())? })
}
/// Serialize a Ed25519 signature to an unpadded base64
/// representation.
#[wasm_bindgen(js_name = "toBase64")]
pub fn to_base64(&self) -> String {
self.inner.to_base64()
}
}
/// A Curve25519 public key.
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct Curve25519PublicKey {
inner: vodozemac::Curve25519PublicKey,
}
#[wasm_bindgen]
impl Curve25519PublicKey {
/// The number of bytes a Curve25519 public key has.
#[wasm_bindgen(getter)]
pub fn length(&self) -> usize {
vodozemac::Curve25519PublicKey::LENGTH
}
/// Serialize an Curve25519 public key to an unpadded base64
/// representation.
#[wasm_bindgen(js_name = "toBase64")]
pub fn to_base64(&self) -> String {
self.inner.to_base64()
}
}
impl_from_to_inner!(vodozemac::Curve25519PublicKey => Curve25519PublicKey);
/// Struct holding the two public identity keys of an account.
#[wasm_bindgen(getter_with_clone)]
#[derive(Debug)]
pub struct IdentityKeys {
/// The Ed25519 public key, used for signing.
pub ed25519: Ed25519PublicKey,
/// The Curve25519 public key, used for establish shared secrets.
pub curve25519: Curve25519PublicKey,
}
impl From<matrix_sdk_crypto::olm::IdentityKeys> for IdentityKeys {
fn from(value: matrix_sdk_crypto::olm::IdentityKeys) -> Self {
Self {
ed25519: Ed25519PublicKey { inner: value.ed25519 },
curve25519: Curve25519PublicKey { inner: value.curve25519 },
}
}
}
/// An enum over the different key types a device can have.
///
/// Currently devices have a curve25519 and ed25519 keypair. The keys
/// transport format is a base64 encoded string, any unknown key type
/// will be left as such a string.
#[wasm_bindgen]
#[derive(Debug)]
pub struct DeviceKey {
inner: matrix_sdk_crypto::types::DeviceKey,
}
impl_from_to_inner!(matrix_sdk_crypto::types::DeviceKey => DeviceKey);
#[wasm_bindgen]
impl DeviceKey {
/// Get the name of the device key.
#[wasm_bindgen(getter)]
pub fn name(&self) -> DeviceKeyName {
(&self.inner).into()
}
/// Get the value associated to the `Curve25519` device key name.
#[wasm_bindgen(getter)]
pub fn curve25519(&self) -> Option<Curve25519PublicKey> {
use matrix_sdk_crypto::types::DeviceKey::*;
match &self.inner {
Curve25519(key) => Some((*key).into()),
_ => None,
}
}
/// Get the value associated to the `Ed25519` device key name.
#[wasm_bindgen(getter)]
pub fn ed25519(&self) -> Option<Ed25519PublicKey> {
use matrix_sdk_crypto::types::DeviceKey::*;
match &self.inner {
Ed25519(key) => Some((*key).into()),
_ => None,
}
}
/// Get the value associated to the `Unknown` device key name.
#[wasm_bindgen(getter)]
pub fn unknown(&self) -> Option<String> {
use matrix_sdk_crypto::types::DeviceKey::*;
match &self.inner {
Unknown(key) => Some(key.clone()),
_ => None,
}
}
/// Convert the `DeviceKey` into a base64 encoded string.
#[wasm_bindgen(js_name = "toBase64")]
pub fn to_base64(&self) -> String {
self.inner.to_base64()
}
}
impl From<&matrix_sdk_crypto::types::DeviceKey> for DeviceKeyName {
fn from(device_key: &matrix_sdk_crypto::types::DeviceKey) -> Self {
use matrix_sdk_crypto::types::DeviceKey::*;
match device_key {
Curve25519(_) => Self::Curve25519,
Ed25519(_) => Self::Ed25519,
Unknown(_) => Self::Unknown,
}
}
}
/// An enum over the different key types a device can have.
///
/// Currently devices have a curve25519 and ed25519 keypair. The keys
/// transport format is a base64 encoded string, any unknown key type
/// will be left as such a string.
#[wasm_bindgen]
#[derive(Debug)]
pub enum DeviceKeyName {
/// The curve25519 device key.
Curve25519,
/// The ed25519 device key.
Ed25519,
/// An unknown device key.
Unknown,
}
@@ -0,0 +1,17 @@
const { UserId, initAsync } = require("../pkg/matrix_sdk_crypto_js");
test("can instantiate rust objects with async initialiser", async () => {
initUserId = () => new UserId("@foo:bar.org");
// stub out the synchronous WebAssembly loader with one that raises an error
jest.spyOn(WebAssembly, "Module").mockImplementation(() => {
throw new Error("synchronous WebAssembly.Module() not allowed");
});
// this should fail
expect(initUserId).toThrow(/synchronous/);
// but once we init with async, it should work
await initAsync();
initUserId();
});
@@ -0,0 +1,83 @@
const { Attachment, EncryptedAttachment } = require("../pkg/matrix_sdk_crypto_js");
describe(Attachment.name, () => {
const originalData = "hello";
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
let encryptedAttachment;
test("can encrypt data", () => {
encryptedAttachment = Attachment.encrypt(textEncoder.encode(originalData));
const mediaEncryptionInfo = JSON.parse(encryptedAttachment.mediaEncryptionInfo);
expect(mediaEncryptionInfo).toMatchObject({
v: "v2",
key: {
kty: expect.any(String),
key_ops: expect.arrayContaining(["encrypt", "decrypt"]),
alg: expect.any(String),
k: expect.any(String),
ext: expect.any(Boolean),
},
iv: expect.stringMatching(/^[A-Za-z0-9\+/]+$/),
hashes: {
sha256: expect.stringMatching(/^[A-Za-z0-9\+/]+$/),
},
});
const encryptedData = encryptedAttachment.encryptedData;
expect(
encryptedData.every((i) => {
i != 0;
}),
).toStrictEqual(false);
});
test("can decrypt data", () => {
expect(encryptedAttachment.hasMediaEncryptionInfoBeenConsumed).toStrictEqual(false);
const decryptedAttachment = Attachment.decrypt(encryptedAttachment);
expect(textDecoder.decode(decryptedAttachment)).toStrictEqual(originalData);
expect(encryptedAttachment.hasMediaEncryptionInfoBeenConsumed).toStrictEqual(true);
});
test("can only decrypt once", () => {
expect(encryptedAttachment.hasMediaEncryptionInfoBeenConsumed).toStrictEqual(true);
expect(() => {
textDecoder.decode(decryptedAttachment);
}).toThrow();
});
});
describe(EncryptedAttachment.name, () => {
const originalData = "hello";
const textDecoder = new TextDecoder();
test("can be created manually", () => {
const encryptedAttachment = new EncryptedAttachment(
new Uint8Array([24, 150, 67, 37, 144]),
JSON.stringify({
v: "v2",
key: {
kty: "oct",
key_ops: ["encrypt", "decrypt"],
alg: "A256CTR",
k: "QbNXUjuukFyEJ8cQZjJuzN6mMokg0HJIjx0wVMLf5BM",
ext: true,
},
iv: "xk2AcWkomiYAAAAAAAAAAA",
hashes: {
sha256: "JsRbDXgOja4xvDiF3DwBuLHdxUzIrVYIuj7W/t3aEok",
},
}),
);
expect(encryptedAttachment.hasMediaEncryptionInfoBeenConsumed).toStrictEqual(false);
expect(textDecoder.decode(Attachment.decrypt(encryptedAttachment))).toStrictEqual(originalData);
expect(encryptedAttachment.hasMediaEncryptionInfoBeenConsumed).toStrictEqual(true);
});
});
@@ -0,0 +1,957 @@
const {
OlmMachine,
UserId,
DeviceId,
DeviceKeyId,
RoomId,
Device,
LocalTrust,
UserDevices,
DeviceKey,
DeviceKeyName,
DeviceKeyAlgorithmName,
Ed25519PublicKey,
EncryptionAlgorithm,
Curve25519PublicKey,
Signatures,
VerificationMethod,
VerificationRequest,
ToDeviceRequest,
DeviceLists,
KeysUploadRequest,
RequestType,
KeysQueryRequest,
Sas,
Emoji,
SigningKeysUploadRequest,
SignatureUploadRequest,
Qr,
QrCode,
QrCodeScan,
} = require("../pkg/matrix_sdk_crypto_js");
const { zip, addMachineToMachine } = require("./helper");
describe("LocalTrust", () => {
test("has the correct variant values", () => {
expect(LocalTrust.Verified).toStrictEqual(0);
expect(LocalTrust.BlackListed).toStrictEqual(1);
expect(LocalTrust.Ignored).toStrictEqual(2);
expect(LocalTrust.Unset).toStrictEqual(3);
});
});
describe("DeviceKeyName", () => {
test("has the correct variant values", () => {
expect(DeviceKeyName.Curve25519).toStrictEqual(0);
expect(DeviceKeyName.Ed25519).toStrictEqual(1);
expect(DeviceKeyName.Unknown).toStrictEqual(2);
});
});
describe(OlmMachine.name, () => {
const user = new UserId("@alice:example.org");
const device = new DeviceId("foobar");
const room = new RoomId("!baz:matrix.org");
function machine(new_user, new_device) {
return OlmMachine.initialize(new_user || user, new_device || device);
}
test("can read user devices", async () => {
const m = await machine();
const userDevices = await m.getUserDevices(user);
expect(userDevices).toBeInstanceOf(UserDevices);
expect(userDevices.get(device)).toBeInstanceOf(Device);
expect(userDevices.isAnyVerified()).toStrictEqual(false);
expect(userDevices.keys().map((device_id) => device_id.toString())).toStrictEqual([device.toString()]);
expect(userDevices.devices().map((device) => device.deviceId.toString())).toStrictEqual([device.toString()]);
});
test("can read a user device", async () => {
const m = await machine();
const hypothetical_response = JSON.stringify({
device_keys: {
"@alice:example.org": {
JLAFKJWSCS: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "JLAFKJWSCS",
keys: {
"curve25519:JLAFKJWSCS": "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4",
"ed25519:JLAFKJWSCS": "nE6W2fCblxDcOFmeEtCHNl8/l8bXcu7GKyAswA4r3mM",
},
signatures: {
"@alice:example.org": {
"ed25519:JLAFKJWSCS":
"m53Wkbh2HXkc3vFApZvCrfXcX3AI51GsDHustMhKwlv3TuOJMj4wistcOTM8q2+e/Ro7rWFUb9ZfnNbwptSUBA",
},
},
unsigned: {
device_display_name: "Alice's mobile phone",
},
user_id: "@alice:example.org",
},
},
},
failures: {},
});
// Insert another device into the store
await m.markRequestAsSent("ID", RequestType.KeysQuery, hypothetical_response);
const secondDeviceId = new DeviceId("JLAFKJWSCS");
const dev = await m.getDevice(user, secondDeviceId);
expect(dev).toBeInstanceOf(Device);
expect(dev.isVerified()).toStrictEqual(false);
expect(dev.isCrossSigningTrusted()).toStrictEqual(false);
expect(dev.isCrossSignedByOwner()).toStrictEqual(false);
expect(dev.localTrustState).toStrictEqual(LocalTrust.Unset);
expect(dev.isLocallyTrusted()).toStrictEqual(false);
expect(await dev.setLocalTrust(LocalTrust.Verified)).toBeNull();
expect(dev.localTrustState).toStrictEqual(LocalTrust.Verified);
expect(dev.isLocallyTrusted()).toStrictEqual(true);
expect(dev.userId.toString()).toStrictEqual(user.toString());
expect(dev.deviceId.toString()).toStrictEqual(secondDeviceId.toString());
expect(dev.deviceName).toBeUndefined();
expect(dev.algorithms).toEqual([
EncryptionAlgorithm.OlmV1Curve25519AesSha2,
EncryptionAlgorithm.MegolmV1AesSha2,
]);
const deviceKey = dev.getKey(DeviceKeyAlgorithmName.Ed25519);
expect(deviceKey).toBeInstanceOf(DeviceKey);
expect(deviceKey.name).toStrictEqual(DeviceKeyName.Ed25519);
expect(deviceKey.curve25519).toBeUndefined();
expect(deviceKey.ed25519).toBeInstanceOf(Ed25519PublicKey);
expect(deviceKey.unknown).toBeUndefined();
expect(deviceKey.toBase64()).toMatch(/^[A-Za-z0-9\+/]+$/);
expect(dev.curve25519Key).toBeInstanceOf(Curve25519PublicKey);
expect(dev.ed25519Key).toBeInstanceOf(Ed25519PublicKey);
for (const [deviceKeyId, deviceKey] of dev.keys) {
expect(deviceKeyId).toBeInstanceOf(DeviceKeyId);
expect(deviceKey).toBeInstanceOf(DeviceKey);
}
expect(dev.signatures).toBeInstanceOf(Signatures);
expect(dev.isBlacklisted()).toStrictEqual(false);
expect(dev.isDeleted()).toStrictEqual(false);
});
});
describe("Key Verification", () => {
const userId1 = new UserId("@alice:example.org");
const deviceId1 = new DeviceId("alice_device");
const userId2 = new UserId("@bob:example.org");
const deviceId2 = new DeviceId("bob_device");
function machine(new_user, new_device) {
return OlmMachine.initialize(new_user || userId1, new_device || deviceId1);
}
describe("SAS", () => {
// First Olm machine.
let m1;
// Second Olm machine.
let m2;
beforeAll(async () => {
m1 = await machine(userId1, deviceId1);
m2 = await machine(userId2, deviceId2);
});
// Verification request for `m1`.
let verificationRequest1;
// The flow ID.
let flowId;
test("can request verification (`m.key.verification.request`)", async () => {
// Make `m1` and `m2` be aware of each other.
{
await addMachineToMachine(m2, m1);
await addMachineToMachine(m1, m2);
}
// Pick the device we want to start the verification with.
const device2 = await m1.getDevice(userId2, deviceId2);
expect(device2).toBeInstanceOf(Device);
let outgoingVerificationRequest;
// Request a verification from `m1` to `device2`.
[verificationRequest1, outgoingVerificationRequest] = await device2.requestVerification();
expect(verificationRequest1).toBeInstanceOf(VerificationRequest);
expect(verificationRequest1.ownUserId.toString()).toStrictEqual(userId1.toString());
expect(verificationRequest1.otherUserId.toString()).toStrictEqual(userId2.toString());
expect(verificationRequest1.otherDeviceId).toBeUndefined();
expect(verificationRequest1.roomId).toBeUndefined();
expect(verificationRequest1.cancelInfo).toBeUndefined();
expect(verificationRequest1.isPassive()).toStrictEqual(false);
expect(verificationRequest1.isReady()).toStrictEqual(false);
expect(verificationRequest1.timedOut()).toStrictEqual(false);
expect(verificationRequest1.theirSupportedMethods).toBeUndefined();
expect(verificationRequest1.ourSupportedMethods).toEqual(
expect.arrayContaining([VerificationMethod.SasV1, VerificationMethod.ReciprocateV1]),
);
expect(verificationRequest1.flowId).toMatch(/^[a-f0-9]+$/);
expect(verificationRequest1.isSelfVerification()).toStrictEqual(false);
expect(verificationRequest1.weStarted()).toStrictEqual(true);
expect(verificationRequest1.isDone()).toStrictEqual(false);
expect(verificationRequest1.isCancelled()).toStrictEqual(false);
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
expect(outgoingVerificationRequest.event_type).toStrictEqual("m.key.verification.request");
const toDeviceEvents = [
{
sender: userId1.toString(),
type: outgoingVerificationRequest.event_type,
content: JSON.parse(outgoingVerificationRequest.body).messages[userId2.toString()][
deviceId2.toString()
],
},
];
// Let's send the verification request to `m2`.
await m2.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
flowId = verificationRequest1.flowId;
});
// Verification request for `m2`.
let verificationRequest2;
test("can fetch received request verification", async () => {
// Oh, a new verification request.
verificationRequest2 = m2.getVerificationRequest(userId1, flowId);
expect(verificationRequest2).toBeInstanceOf(VerificationRequest);
expect(verificationRequest2.ownUserId.toString()).toStrictEqual(userId2.toString());
expect(verificationRequest2.otherUserId.toString()).toStrictEqual(userId1.toString());
expect(verificationRequest2.otherDeviceId.toString()).toStrictEqual(deviceId1.toString());
expect(verificationRequest2.roomId).toBeUndefined();
expect(verificationRequest2.cancelInfo).toBeUndefined();
expect(verificationRequest2.isPassive()).toStrictEqual(false);
expect(verificationRequest2.isReady()).toStrictEqual(false);
expect(verificationRequest2.timedOut()).toStrictEqual(false);
expect(verificationRequest2.theirSupportedMethods).toEqual(
expect.arrayContaining([VerificationMethod.SasV1, VerificationMethod.ReciprocateV1]),
);
expect(verificationRequest2.ourSupportedMethods).toBeUndefined();
expect(verificationRequest2.flowId).toStrictEqual(flowId);
expect(verificationRequest2.isSelfVerification()).toStrictEqual(false);
expect(verificationRequest2.weStarted()).toStrictEqual(false);
expect(verificationRequest2.isDone()).toStrictEqual(false);
expect(verificationRequest2.isCancelled()).toStrictEqual(false);
const verificationRequests = m2.getVerificationRequests(userId1);
expect(verificationRequests).toHaveLength(1);
expect(verificationRequests[0].flowId).toStrictEqual(verificationRequest2.flowId); // there are the same
});
test("can accept a verification request (`m.key.verification.ready`)", async () => {
// Accept the verification request.
let outgoingVerificationRequest = verificationRequest2.accept();
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
// The request verification is ready.
expect(outgoingVerificationRequest.event_type).toStrictEqual("m.key.verification.ready");
const toDeviceEvents = [
{
sender: userId2.toString(),
type: outgoingVerificationRequest.event_type,
content: JSON.parse(outgoingVerificationRequest.body).messages[userId1.toString()][
deviceId1.toString()
],
},
];
// Let's send the verification ready to `m1`.
await m1.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
});
test("verification requests are synchronized and automatically updated", () => {
expect(verificationRequest1.isReady()).toStrictEqual(true);
expect(verificationRequest2.isReady()).toStrictEqual(true);
expect(verificationRequest1.theirSupportedMethods).toEqual(
expect.arrayContaining([VerificationMethod.SasV1, VerificationMethod.ReciprocateV1]),
);
expect(verificationRequest1.ourSupportedMethods).toEqual(
expect.arrayContaining([VerificationMethod.SasV1, VerificationMethod.ReciprocateV1]),
);
expect(verificationRequest2.theirSupportedMethods).toEqual(
expect.arrayContaining([VerificationMethod.SasV1, VerificationMethod.ReciprocateV1]),
);
expect(verificationRequest2.ourSupportedMethods).toEqual(
expect.arrayContaining([VerificationMethod.SasV1, VerificationMethod.ReciprocateV1]),
);
});
// SAS verification for the second machine.
let sas2;
test("can start a SAS verification (`m.key.verification.start`)", async () => {
// Let's start a SAS verification, from `m2` for example.
[sas2, outgoingVerificationRequest] = await verificationRequest2.startSas();
expect(sas2).toBeInstanceOf(Sas);
expect(sas2.userId.toString()).toStrictEqual(userId2.toString());
expect(sas2.deviceId.toString()).toStrictEqual(deviceId2.toString());
expect(sas2.otherUserId.toString()).toStrictEqual(userId1.toString());
expect(sas2.otherDeviceId.toString()).toStrictEqual(deviceId1.toString());
expect(sas2.flowId).toStrictEqual(flowId);
expect(sas2.roomId).toBeUndefined();
expect(sas2.supportsEmoji()).toStrictEqual(false);
expect(sas2.startedFromRequest()).toStrictEqual(true);
expect(sas2.isSelfVerification()).toStrictEqual(false);
expect(sas2.haveWeConfirmed()).toStrictEqual(false);
expect(sas2.hasBeenAccepted()).toStrictEqual(false);
expect(sas2.cancelInfo()).toBeUndefined();
expect(sas2.weStarted()).toStrictEqual(false);
expect(sas2.timedOut()).toStrictEqual(false);
expect(sas2.canBePresented()).toStrictEqual(false);
expect(sas2.isDone()).toStrictEqual(false);
expect(sas2.isCancelled()).toStrictEqual(false);
expect(sas2.emoji()).toBeUndefined();
expect(sas2.emojiIndex()).toBeUndefined();
expect(sas2.decimals()).toBeUndefined();
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
expect(outgoingVerificationRequest.event_type).toStrictEqual("m.key.verification.start");
const toDeviceEvents = [
{
sender: userId2.toString(),
type: outgoingVerificationRequest.event_type,
content: JSON.parse(outgoingVerificationRequest.body).messages[userId1.toString()][
deviceId1.toString()
],
},
];
// Let's send the SAS start to `m1`.
await m1.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
});
// SAS verification for the second machine.
let sas1;
test("can fetch and accept an ongoing SAS verification (`m.key.verification.accept`)", async () => {
// Let's fetch the ongoing SAS verification.
sas1 = await m1.getVerification(userId2, flowId);
expect(sas1).toBeInstanceOf(Sas);
expect(sas1.userId.toString()).toStrictEqual(userId1.toString());
expect(sas1.deviceId.toString()).toStrictEqual(deviceId1.toString());
expect(sas1.otherUserId.toString()).toStrictEqual(userId2.toString());
expect(sas1.otherDeviceId.toString()).toStrictEqual(deviceId2.toString());
expect(sas1.flowId).toStrictEqual(flowId);
expect(sas1.roomId).toBeUndefined();
expect(sas1.startedFromRequest()).toStrictEqual(true);
expect(sas1.isSelfVerification()).toStrictEqual(false);
expect(sas1.haveWeConfirmed()).toStrictEqual(false);
expect(sas1.hasBeenAccepted()).toStrictEqual(false);
expect(sas1.cancelInfo()).toBeUndefined();
expect(sas1.weStarted()).toStrictEqual(true);
expect(sas1.timedOut()).toStrictEqual(false);
expect(sas1.canBePresented()).toStrictEqual(false);
expect(sas1.isDone()).toStrictEqual(false);
expect(sas1.isCancelled()).toStrictEqual(false);
expect(sas1.emoji()).toBeUndefined();
expect(sas1.emojiIndex()).toBeUndefined();
expect(sas1.decimals()).toBeUndefined();
// Let's accept thet SAS start request.
let outgoingVerificationRequest = sas1.accept();
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
expect(outgoingVerificationRequest.event_type).toStrictEqual("m.key.verification.accept");
const toDeviceEvents = [
{
sender: userId1.toString(),
type: outgoingVerificationRequest.event_type,
content: JSON.parse(outgoingVerificationRequest.body).messages[userId2.toString()][
deviceId2.toString()
],
},
];
// Let's send the SAS accept to `m2`.
await m2.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
});
test("emojis are supported by both sides", () => {
expect(sas1.supportsEmoji()).toStrictEqual(true);
expect(sas2.supportsEmoji()).toStrictEqual(true);
});
test("one side sends verification key (`m.key.verification.key`)", async () => {
// Let's send the verification keys from `m2` to `m1`.
const outgoingRequests = await m2.outgoingRequests();
let toDeviceRequest = outgoingRequests.find((request) => request.type == RequestType.ToDevice);
expect(toDeviceRequest).toBeInstanceOf(ToDeviceRequest);
expect(toDeviceRequest.event_type).toStrictEqual("m.key.verification.key");
const toDeviceEvents = [
{
sender: userId2.toString(),
type: toDeviceRequest.event_type,
content: JSON.parse(toDeviceRequest.body).messages[userId1.toString()][deviceId1.toString()],
},
];
// Let's send te SAS key to `m1`.
await m1.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
m2.markRequestAsSent(toDeviceRequest.id, toDeviceRequest.type, "{}");
});
test("other side sends back verification key (`m.key.verification.key`)", async () => {
// Let's send the verification keys from `m1` to `m2`.
const outgoingRequests = await m1.outgoingRequests();
let toDeviceRequest = outgoingRequests.find((request) => request.type == RequestType.ToDevice);
expect(toDeviceRequest).toBeInstanceOf(ToDeviceRequest);
expect(toDeviceRequest.event_type).toStrictEqual("m.key.verification.key");
const toDeviceEvents = [
{
sender: userId1.toString(),
type: toDeviceRequest.event_type,
content: JSON.parse(toDeviceRequest.body).messages[userId2.toString()][deviceId2.toString()],
},
];
// Let's send te SAS key to `m2`.
await m2.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
m1.markRequestAsSent(toDeviceRequest.id, toDeviceRequest.type, "{}");
});
test("emojis match from both sides", () => {
const emojis1 = sas1.emoji();
const emojiIndexes1 = sas1.emojiIndex();
const emojis2 = sas2.emoji();
const emojiIndexes2 = sas2.emojiIndex();
expect(emojis1).toHaveLength(7);
expect(emojiIndexes1).toHaveLength(emojis1.length);
expect(emojis2).toHaveLength(emojis1.length);
expect(emojiIndexes2).toHaveLength(emojis1.length);
const isEmoji =
/(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/;
for (const [emoji1, emojiIndex1, emoji2, emojiIndex2] of zip(
emojis1,
emojiIndexes1,
emojis2,
emojiIndexes2,
)) {
expect(emoji1).toBeInstanceOf(Emoji);
expect(emoji1.symbol).toMatch(isEmoji);
expect(emoji1.description).toBeTruthy();
expect(emojiIndex1).toBeGreaterThanOrEqual(0);
expect(emojiIndex1).toBeLessThanOrEqual(63);
expect(emoji2).toBeInstanceOf(Emoji);
expect(emoji2.symbol).toStrictEqual(emoji1.symbol);
expect(emoji2.description).toStrictEqual(emoji1.description);
expect(emojiIndex2).toStrictEqual(emojiIndex1);
}
});
test("decimals match from both sides", () => {
const decimals1 = sas1.decimals();
const decimals2 = sas2.decimals();
expect(decimals1).toHaveLength(3);
expect(decimals2).toHaveLength(decimals1.length);
const isDecimal = /^[0-9]{4}$/;
for (const [decimal1, decimal2] of zip(decimals1, decimals2)) {
expect(decimal1.toString()).toMatch(isDecimal);
expect(decimal2).toStrictEqual(decimal1);
}
});
test("can confirm keys match (`m.key.verification.mac`)", async () => {
// `m1` confirms.
const [outgoingVerificationRequests, signatureUploadRequest] = await sas1.confirm();
expect(signatureUploadRequest).toBeUndefined();
expect(outgoingVerificationRequests).toHaveLength(1);
let outgoingVerificationRequest = outgoingVerificationRequests[0];
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
expect(outgoingVerificationRequest.event_type).toStrictEqual("m.key.verification.mac");
const toDeviceEvents = [
{
sender: userId1.toString(),
type: outgoingVerificationRequest.event_type,
content: JSON.parse(outgoingVerificationRequest.body).messages[userId2.toString()][
deviceId2.toString()
],
},
];
// Let's send te SAS confirmation to `m2`.
await m2.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
});
test("can confirm back keys match (`m.key.verification.done`)", async () => {
// `m2` confirms.
const [outgoingVerificationRequests, signatureUploadRequest] = await sas2.confirm();
expect(signatureUploadRequest).toBeUndefined();
expect(outgoingVerificationRequests).toHaveLength(2);
// `.mac`
{
let outgoingVerificationRequest = outgoingVerificationRequests[0];
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
expect(outgoingVerificationRequest.event_type).toStrictEqual("m.key.verification.mac");
const toDeviceEvents = [
{
sender: userId2.toString(),
type: outgoingVerificationRequest.event_type,
content: JSON.parse(outgoingVerificationRequest.body).messages[userId1.toString()][
deviceId1.toString()
],
},
];
// Let's send te SAS confirmation to `m1`.
await m1.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
}
// `.done`
{
let outgoingVerificationRequest = outgoingVerificationRequests[1];
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
expect(outgoingVerificationRequest.event_type).toStrictEqual("m.key.verification.done");
const toDeviceEvents = [
{
sender: userId2.toString(),
type: outgoingVerificationRequest.event_type,
content: JSON.parse(outgoingVerificationRequest.body).messages[userId1.toString()][
deviceId1.toString()
],
},
];
// Let's send te SAS done to `m1`.
await m1.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
}
});
test("can send final done (`m.key.verification.done`)", async () => {
const outgoingRequests = await m1.outgoingRequests();
expect(outgoingRequests).toHaveLength(4);
let toDeviceRequest = outgoingRequests.find((request) => request.type == RequestType.ToDevice);
expect(toDeviceRequest).toBeInstanceOf(ToDeviceRequest);
expect(toDeviceRequest.event_type).toStrictEqual("m.key.verification.done");
const toDeviceEvents = [
{
sender: userId1.toString(),
type: toDeviceRequest.event_type,
content: JSON.parse(toDeviceRequest.body).messages[userId2.toString()][deviceId2.toString()],
},
];
// Let's send te SAS key to `m2`.
await m2.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
m1.markRequestAsSent(toDeviceRequest.id, toDeviceRequest.type, "{}");
});
test("can see if verification is done", () => {
expect(verificationRequest1.isDone()).toStrictEqual(true);
expect(verificationRequest2.isDone()).toStrictEqual(true);
expect(sas1.isDone()).toStrictEqual(true);
expect(sas2.isDone()).toStrictEqual(true);
});
});
describe("QR Code", () => {
if (undefined === Qr) {
// qrcode supports is not enabled
console.info("qrcode support is disabled, skip the associated test suite");
return;
}
// First Olm machine.
let m1;
// Second Olm machine.
let m2;
beforeAll(async () => {
m1 = await machine(userId1, deviceId1);
m2 = await machine(userId2, deviceId2);
});
// Verification request for `m1`.
let verificationRequest1;
// The flow ID.
let flowId;
test("can request verification (`m.key.verification.request`)", async () => {
// Make `m1` and `m2` be aware of each other.
{
await addMachineToMachine(m2, m1);
await addMachineToMachine(m1, m2);
}
// Pick the device we want to start the verification with.
const device2 = await m1.getDevice(userId2, deviceId2);
expect(device2).toBeInstanceOf(Device);
let outgoingVerificationRequest;
// Request a verification from `m1` to `device2`.
[verificationRequest1, outgoingVerificationRequest] = await device2.requestVerification([
VerificationMethod.QrCodeScanV1, // by default
VerificationMethod.QrCodeShowV1, // the one we add
]);
expect(verificationRequest1).toBeInstanceOf(VerificationRequest);
expect(verificationRequest1.ownUserId.toString()).toStrictEqual(userId1.toString());
expect(verificationRequest1.otherUserId.toString()).toStrictEqual(userId2.toString());
expect(verificationRequest1.otherDeviceId).toBeUndefined();
expect(verificationRequest1.roomId).toBeUndefined();
expect(verificationRequest1.cancelInfo).toBeUndefined();
expect(verificationRequest1.isPassive()).toStrictEqual(false);
expect(verificationRequest1.isReady()).toStrictEqual(false);
expect(verificationRequest1.timedOut()).toStrictEqual(false);
expect(verificationRequest1.theirSupportedMethods).toBeUndefined();
expect(verificationRequest1.ourSupportedMethods).toEqual(
expect.arrayContaining([VerificationMethod.QrCodeShowV1]),
);
expect(verificationRequest1.flowId).toMatch(/^[a-f0-9]+$/);
expect(verificationRequest1.isSelfVerification()).toStrictEqual(false);
expect(verificationRequest1.weStarted()).toStrictEqual(true);
expect(verificationRequest1.isDone()).toStrictEqual(false);
expect(verificationRequest1.isCancelled()).toStrictEqual(false);
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
expect(outgoingVerificationRequest.event_type).toStrictEqual("m.key.verification.request");
const toDeviceEvents = [
{
sender: userId1.toString(),
type: outgoingVerificationRequest.event_type,
content: JSON.parse(outgoingVerificationRequest.body).messages[userId2.toString()][
deviceId2.toString()
],
},
];
// Let's send the verification request to `m2`.
await m2.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
flowId = verificationRequest1.flowId;
});
// Verification request for `m2`.
let verificationRequest2;
test("can fetch received request verification", async () => {
// Oh, a new verification request.
verificationRequest2 = m2.getVerificationRequest(userId1, flowId);
expect(verificationRequest2).toBeInstanceOf(VerificationRequest);
expect(verificationRequest2.ownUserId.toString()).toStrictEqual(userId2.toString());
expect(verificationRequest2.otherUserId.toString()).toStrictEqual(userId1.toString());
expect(verificationRequest2.otherDeviceId.toString()).toStrictEqual(deviceId1.toString());
expect(verificationRequest2.roomId).toBeUndefined();
expect(verificationRequest2.cancelInfo).toBeUndefined();
expect(verificationRequest2.isPassive()).toStrictEqual(false);
expect(verificationRequest2.isReady()).toStrictEqual(false);
expect(verificationRequest2.timedOut()).toStrictEqual(false);
expect(verificationRequest2.theirSupportedMethods).toEqual(
expect.arrayContaining([VerificationMethod.QrCodeScanV1, VerificationMethod.QrCodeShowV1]),
);
expect(verificationRequest2.ourSupportedMethods).toBeUndefined();
expect(verificationRequest2.flowId).toStrictEqual(flowId);
expect(verificationRequest2.isSelfVerification()).toStrictEqual(false);
expect(verificationRequest2.weStarted()).toStrictEqual(false);
expect(verificationRequest2.isDone()).toStrictEqual(false);
expect(verificationRequest2.isCancelled()).toStrictEqual(false);
const verificationRequests = m2.getVerificationRequests(userId1);
expect(verificationRequests).toHaveLength(1);
expect(verificationRequests[0].flowId).toStrictEqual(verificationRequest2.flowId); // there are the same
});
test("can accept a verification request with methods (`m.key.verification.ready`)", async () => {
// Accept the verification request.
let outgoingVerificationRequest = verificationRequest2.acceptWithMethods([
VerificationMethod.QrCodeScanV1, // by default
VerificationMethod.QrCodeShowV1, // the one we add
]);
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
// The request verification is ready.
expect(outgoingVerificationRequest.event_type).toStrictEqual("m.key.verification.ready");
const toDeviceEvents = [
{
sender: userId2.toString(),
type: outgoingVerificationRequest.event_type,
content: JSON.parse(outgoingVerificationRequest.body).messages[userId1.toString()][
deviceId1.toString()
],
},
];
// Let's send the verification ready to `m1`.
await m1.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
});
test("verification requests are synchronized and automatically updated", () => {
expect(verificationRequest1.isReady()).toStrictEqual(true);
expect(verificationRequest2.isReady()).toStrictEqual(true);
expect(verificationRequest1.theirSupportedMethods).toEqual(
expect.arrayContaining([VerificationMethod.QrCodeScanV1, VerificationMethod.QrCodeShowV1]),
);
expect(verificationRequest1.ourSupportedMethods).toEqual(
expect.arrayContaining([VerificationMethod.QrCodeScanV1, VerificationMethod.QrCodeShowV1]),
);
expect(verificationRequest2.theirSupportedMethods).toEqual(
expect.arrayContaining([VerificationMethod.QrCodeScanV1, VerificationMethod.QrCodeShowV1]),
);
expect(verificationRequest2.ourSupportedMethods).toEqual(
expect.arrayContaining([VerificationMethod.QrCodeScanV1, VerificationMethod.QrCodeShowV1]),
);
});
// QR verification for the second machine.
let qr2;
test("can generate a QR code", async () => {
qr2 = await verificationRequest2.generateQrCode();
expect(qr2).toBeInstanceOf(Qr);
expect(qr2.hasBeenScanned()).toStrictEqual(false);
expect(qr2.hasBeenConfirmed()).toStrictEqual(false);
expect(qr2.userId.toString()).toStrictEqual(userId2.toString());
expect(qr2.otherUserId.toString()).toStrictEqual(userId1.toString());
expect(qr2.otherDeviceId.toString()).toStrictEqual(deviceId1.toString());
expect(qr2.weStarted()).toStrictEqual(false);
expect(qr2.cancelInfo()).toBeUndefined();
expect(qr2.isDone()).toStrictEqual(false);
expect(qr2.isCancelled()).toStrictEqual(false);
expect(qr2.isSelfVerification()).toStrictEqual(false);
expect(qr2.reciprocated()).toStrictEqual(false);
expect(qr2.flowId).toMatch(/^[a-f0-9]+$/);
expect(qr2.roomId).toBeUndefined();
});
test("can read QR code's bytes", async () => {
const qrCodeHeader = "MATRIX";
const qrCodeVersion = "\x02";
const qrCodeBytes = qr2.toBytes();
expect(qrCodeBytes).toHaveLength(122);
expect(Array.from(qrCodeBytes.slice(0, 7))).toEqual(
[...qrCodeHeader, ...qrCodeVersion].map((char) => char.charCodeAt(0)),
);
});
test("can render QR code", async () => {
const qrCode = qr2.toQrCode();
expect(qrCode).toBeInstanceOf(QrCode);
// Want to get `canvasBuffer` to render the QR code? Install `npm install canvas` and uncomment the following blocks.
//let canvasBuffer;
{
const buffer = qrCode.renderIntoBuffer();
expect(buffer).toBeInstanceOf(Uint8ClampedArray);
// 45px ⨉ 45px
expect(buffer).toHaveLength(45 * 45);
// 0 for a white pixel, 1 for a black pixel.
expect(buffer.every((p) => p == 0 || p == 1)).toStrictEqual(true);
/*
const { Canvas } = require('canvas');
const canvas = new Canvas(55, 55);
const context = canvas.getContext('2d');
context.fillStyle = 'white';
context.fillRect(0, 0, canvas.width, canvas.height);
// New image data, filled with black, transparent pixels.
const imageData = context.createImageData(45, 45);
const data = imageData.data;
const [r, g, b, a] = [0, 1, 2, 3];
for (
let dataNth = 0,
bufferNth = 0;
dataNth < data.length && bufferNth < buffer.length;
dataNth += 4,
bufferNth += 1
) {
data[dataNth + a] = 255;
// White pixel
if (buffer[bufferNth] == 0) {
data[dataNth + r] = 255;
data[dataNth + g] = 255;
data[dataNth + b] = 255;
}
}
context.putImageData(imageData, 5, 5);
canvasBuffer = canvas.toBuffer('image/png');
*/
}
// Want to see the QR code? Uncomment the following block.
/*
{
const fs = require('fs/promises');
const path = require('path');
const os = require('os');
const tempDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'matrix-sdk-crypto--'));
const qrCodeFile = path.join(tempDirectory, 'qrcode.png');
console.log(`View the QR code at \`${qrCodeFile}\`.`);
expect(await fs.writeFile(qrCodeFile, canvasBuffer)).toBeUndefined();
}
*/
});
let qr1;
test("can scan a QR code from bytes", async () => {
const scan = QrCodeScan.fromBytes(qr2.toBytes());
expect(scan).toBeInstanceOf(QrCodeScan);
qr1 = await verificationRequest1.scanQrCode(scan);
expect(qr1).toBeInstanceOf(Qr);
expect(qr1.hasBeenScanned()).toStrictEqual(false);
expect(qr1.hasBeenConfirmed()).toStrictEqual(false);
expect(qr1.userId.toString()).toStrictEqual(userId1.toString());
expect(qr1.otherUserId.toString()).toStrictEqual(userId2.toString());
expect(qr1.otherDeviceId.toString()).toStrictEqual(deviceId2.toString());
expect(qr1.weStarted()).toStrictEqual(true);
expect(qr1.cancelInfo()).toBeUndefined();
expect(qr1.isDone()).toStrictEqual(false);
expect(qr1.isCancelled()).toStrictEqual(false);
expect(qr1.isSelfVerification()).toStrictEqual(false);
expect(qr1.reciprocated()).toStrictEqual(true);
expect(qr1.flowId).toMatch(/^[a-f0-9]+$/);
expect(qr1.roomId).toBeUndefined();
});
test("can start a QR verification/reciprocate (`m.key.verification.start`)", async () => {
let outgoingVerificationRequest = qr1.reciprocate();
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
expect(outgoingVerificationRequest.event_type).toStrictEqual("m.key.verification.start");
const toDeviceEvents = [
{
sender: userId1.toString(),
type: outgoingVerificationRequest.event_type,
content: JSON.parse(outgoingVerificationRequest.body).messages[userId2.toString()][
deviceId2.toString()
],
},
];
// Let's send the verification request to `m2`.
await m2.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
});
test("can confirm QR code has been scanned", () => {
expect(qr2.hasBeenScanned()).toStrictEqual(true);
});
test("can confirm scanning (`m.key.verification.done`)", async () => {
let outgoingVerificationRequest = qr2.confirmScanning();
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
expect(outgoingVerificationRequest.event_type).toStrictEqual("m.key.verification.done");
const toDeviceEvents = [
{
sender: userId2.toString(),
type: outgoingVerificationRequest.event_type,
content: JSON.parse(outgoingVerificationRequest.body).messages[userId1.toString()][
deviceId1.toString()
],
},
];
// Let's send the verification request to `m2`.
await m2.receiveSyncChanges(JSON.stringify(toDeviceEvents), new DeviceLists(), new Map(), new Set());
});
test("can confirm QR code has been confirmed", () => {
expect(qr2.hasBeenConfirmed()).toStrictEqual(true);
});
});
});
describe("VerificationMethod", () => {
test("has the correct variant values", () => {
expect(VerificationMethod.SasV1).toStrictEqual(0);
expect(VerificationMethod.QrCodeScanV1).toStrictEqual(1);
expect(VerificationMethod.QrCodeShowV1).toStrictEqual(2);
expect(VerificationMethod.ReciprocateV1).toStrictEqual(3);
});
});
@@ -0,0 +1,35 @@
const {
EncryptionAlgorithm,
EncryptionSettings,
HistoryVisibility,
VerificationState,
} = require("../pkg/matrix_sdk_crypto_js");
describe("EncryptionAlgorithm", () => {
test("has the correct variant values", () => {
expect(EncryptionAlgorithm.OlmV1Curve25519AesSha2).toStrictEqual(0);
expect(EncryptionAlgorithm.MegolmV1AesSha2).toStrictEqual(1);
});
});
describe(EncryptionSettings.name, () => {
test("can be instantiated with default values", () => {
const es = new EncryptionSettings();
expect(es.algorithm).toStrictEqual(EncryptionAlgorithm.MegolmV1AesSha2);
expect(es.rotationPeriod).toStrictEqual(604800000000n);
expect(es.rotationPeriodMessages).toStrictEqual(100n);
expect(es.historyVisibility).toStrictEqual(HistoryVisibility.Shared);
});
test("checks the history visibility values", () => {
const es = new EncryptionSettings();
es.historyVisibility = HistoryVisibility.Invited;
expect(es.historyVisibility).toStrictEqual(HistoryVisibility.Invited);
expect(() => {
es.historyVisibility = 42;
}).toThrow();
});
});
@@ -0,0 +1,10 @@
const { HistoryVisibility } = require("../pkg/matrix_sdk_crypto_js");
describe("HistoryVisibility", () => {
test("has the correct variant values", () => {
expect(HistoryVisibility.Invited).toStrictEqual(0);
expect(HistoryVisibility.Joined).toStrictEqual(1);
expect(HistoryVisibility.Shared).toStrictEqual(2);
expect(HistoryVisibility.WorldReadable).toStrictEqual(3);
});
});
@@ -0,0 +1,92 @@
const { DeviceLists, RequestType, KeysUploadRequest, KeysQueryRequest } = require("../pkg/matrix_sdk_crypto_js");
function* zip(...arrays) {
const len = Math.min(...arrays.map((array) => array.length));
for (let nth = 0; nth < len; ++nth) {
yield [...arrays.map((array) => array.at(nth))];
}
}
// Add a machine to another machine, i.e. be sure a machine knows
// another exists.
async function addMachineToMachine(machineToAdd, machine) {
const toDeviceEvents = JSON.stringify([]);
const changedDevices = new DeviceLists();
const oneTimeKeyCounts = new Map();
const unusedFallbackKeys = new Set();
const receiveSyncChanges = JSON.parse(
await machineToAdd.receiveSyncChanges(toDeviceEvents, changedDevices, oneTimeKeyCounts, unusedFallbackKeys),
);
expect(receiveSyncChanges).toEqual([]);
const outgoingRequests = await machineToAdd.outgoingRequests();
expect(outgoingRequests).toHaveLength(2);
let keysUploadRequest;
// Read the `KeysUploadRequest`.
{
expect(outgoingRequests[0]).toBeInstanceOf(KeysUploadRequest);
expect(outgoingRequests[0].id).toBeDefined();
expect(outgoingRequests[0].type).toStrictEqual(RequestType.KeysUpload);
expect(outgoingRequests[0].body).toBeDefined();
const body = JSON.parse(outgoingRequests[0].body);
expect(body.device_keys).toBeDefined();
expect(body.one_time_keys).toBeDefined();
// https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysupload
const hypothetical_response = JSON.stringify({
one_time_key_counts: {
curve25519: 10,
signed_curve25519: 20,
},
});
const marked = await machineToAdd.markRequestAsSent(
outgoingRequests[0].id,
outgoingRequests[0].type,
hypothetical_response,
);
expect(marked).toStrictEqual(true);
keysUploadRequest = outgoingRequests[0];
}
{
expect(outgoingRequests[1]).toBeInstanceOf(KeysQueryRequest);
let [signingKeysUploadRequest, _] = await machineToAdd.bootstrapCrossSigning(true);
// Let's forge a `KeysQuery`'s response.
let keyQueryResponse = {
device_keys: {},
master_keys: {},
self_signing_keys: {},
user_signing_keys: {},
};
const userId = machineToAdd.userId.toString();
const deviceId = machineToAdd.deviceId.toString();
keyQueryResponse.device_keys[userId] = {};
keyQueryResponse.device_keys[userId][deviceId] = JSON.parse(keysUploadRequest.body).device_keys;
const keys = JSON.parse(signingKeysUploadRequest.body);
keyQueryResponse.master_keys[userId] = keys.master_key;
keyQueryResponse.self_signing_keys[userId] = keys.self_signing_key;
keyQueryResponse.user_signing_keys[userId] = keys.user_signing_key;
const marked = await machine.markRequestAsSent(
outgoingRequests[1].id,
outgoingRequests[1].type,
JSON.stringify(keyQueryResponse),
);
expect(marked).toStrictEqual(true);
}
}
module.exports = {
zip,
addMachineToMachine,
};
@@ -0,0 +1,197 @@
const {
DeviceId,
DeviceKeyAlgorithm,
DeviceKeyAlgorithmName,
DeviceKeyId,
EventId,
RoomId,
ServerName,
UserId,
} = require("../pkg/matrix_sdk_crypto_js");
describe(UserId.name, () => {
test("cannot be invalid", () => {
expect(() => {
new UserId("@foobar");
}).toThrow();
});
const user = new UserId("@foo:bar.org");
test("localpart is present", () => {
expect(user.localpart).toStrictEqual("foo");
});
test("server name is present", () => {
expect(user.serverName).toBeInstanceOf(ServerName);
});
test("user ID is not historical", () => {
expect(user.isHistorical()).toStrictEqual(false);
});
test("can read the user ID as a string", () => {
expect(user.toString()).toStrictEqual("@foo:bar.org");
});
});
describe(DeviceId.name, () => {
const device = new DeviceId("foo");
test("can read the device ID as a string", () => {
expect(device.toString()).toStrictEqual("foo");
});
});
describe(DeviceKeyId.name, () => {
for (const deviceKey of [
{
name: "ed25519",
id: "ed25519:foobar",
algorithmName: DeviceKeyAlgorithmName.Ed25519,
algorithm: "ed25519",
deviceId: "foobar",
},
{
name: "curve25519",
id: "curve25519:foobar",
algorithmName: DeviceKeyAlgorithmName.Curve25519,
algorithm: "curve25519",
deviceId: "foobar",
},
{
name: "signed curve25519",
id: "signed_curve25519:foobar",
algorithmName: DeviceKeyAlgorithmName.SignedCurve25519,
algorithm: "signed_curve25519",
deviceId: "foobar",
},
{
name: "unknown",
id: "hello:foobar",
algorithmName: DeviceKeyAlgorithmName.Unknown,
algorithm: "hello",
deviceId: "foobar",
},
]) {
test(`${deviceKey.name} algorithm`, () => {
const dk = new DeviceKeyId(deviceKey.id);
expect(dk.algorithm.name).toStrictEqual(deviceKey.algorithmName);
expect(dk.algorithm.toString()).toStrictEqual(deviceKey.algorithm);
expect(dk.deviceId.toString()).toStrictEqual(deviceKey.deviceId);
expect(dk.toString()).toStrictEqual(deviceKey.id);
});
}
});
describe("DeviceKeyAlgorithmName", () => {
test("has the correct variants", () => {
expect(DeviceKeyAlgorithmName.Ed25519).toStrictEqual(0);
expect(DeviceKeyAlgorithmName.Curve25519).toStrictEqual(1);
expect(DeviceKeyAlgorithmName.SignedCurve25519).toStrictEqual(2);
expect(DeviceKeyAlgorithmName.Unknown).toStrictEqual(3);
});
});
describe(RoomId.name, () => {
test("cannot be invalid", () => {
expect(() => {
new RoomId("!foo");
}).toThrow();
});
const room = new RoomId("!foo:bar.org");
test("localpart is present", () => {
expect(room.localpart).toStrictEqual("foo");
});
test("server name is present", () => {
expect(room.serverName).toBeInstanceOf(ServerName);
});
test("can read the room ID as string", () => {
expect(room.toString()).toStrictEqual("!foo:bar.org");
});
});
describe(ServerName.name, () => {
test("cannot be invalid", () => {
expect(() => {
new ServerName("@foobar");
}).toThrow();
});
test("host is present", () => {
expect(new ServerName("foo.org").host).toStrictEqual("foo.org");
});
test("port can be optional", () => {
expect(new ServerName("foo.org").port).toStrictEqual(undefined);
expect(new ServerName("foo.org:1234").port).toStrictEqual(1234);
});
test("server is not an IP literal", () => {
expect(new ServerName("foo.org").isIpLiteral()).toStrictEqual(false);
});
});
describe(EventId.name, () => {
test("cannot be invalid", () => {
expect(() => {
new EventId("%foo");
}).toThrow();
});
describe("Versions 1 & 2", () => {
const room = new EventId("$h29iv0s8:foo.org");
test("localpart is present", () => {
expect(room.localpart).toStrictEqual("h29iv0s8");
});
test("server name is present", () => {
expect(room.serverName).toBeInstanceOf(ServerName);
});
test("can read the room ID as string", () => {
expect(room.toString()).toStrictEqual("$h29iv0s8:foo.org");
});
});
describe("Version 3", () => {
const room = new EventId("$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk");
test("localpart is present", () => {
expect(room.localpart).toStrictEqual("acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk");
});
test("server name is present", () => {
expect(room.serverName).toBeUndefined();
});
test("can read the room ID as string", () => {
expect(room.toString()).toStrictEqual("$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk");
});
});
describe("Version 4", () => {
const room = new EventId("$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg");
test("localpart is present", () => {
expect(room.localpart).toStrictEqual("Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg");
});
test("server name is present", () => {
expect(room.serverName).toBeUndefined();
});
test("can read the room ID as string", () => {
expect(room.toString()).toStrictEqual("$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg");
});
});
});
@@ -0,0 +1,872 @@
const {
CrossSigningStatus,
DecryptedRoomEvent,
DeviceId,
DeviceKeyId,
DeviceLists,
EncryptionSettings,
EventId,
InboundGroupSession,
KeysQueryRequest,
KeysUploadRequest,
MaybeSignature,
OlmMachine,
OwnUserIdentity,
RequestType,
RoomId,
RoomMessageRequest,
ShieldColor,
SignatureUploadRequest,
ToDeviceRequest,
UserId,
UserIdentity,
VerificationRequest,
VerificationState,
Versions,
getVersions,
} = require("../pkg/matrix_sdk_crypto_js");
const { addMachineToMachine } = require("./helper");
require("fake-indexeddb/auto");
describe("Versions", () => {
test("can find out the crate versions", async () => {
const versions = getVersions();
expect(versions).toBeInstanceOf(Versions);
expect(versions.vodozemac).toBeDefined();
expect(versions.matrix_sdk_crypto).toBeDefined();
});
});
describe(OlmMachine.name, () => {
test("can be instantiated with the async initializer", async () => {
expect(await OlmMachine.initialize(new UserId("@foo:bar.org"), new DeviceId("baz"))).toBeInstanceOf(OlmMachine);
});
test("can be instantiated with a store", async () => {
let store_name = "hello";
let store_passphrase = "world";
const by_store_name = (db) => db.name.startsWith(store_name);
// No databases.
expect((await indexedDB.databases()).filter(by_store_name)).toHaveLength(0);
// Creating a new Olm machine.
expect(
await OlmMachine.initialize(new UserId("@foo:bar.org"), new DeviceId("baz"), store_name, store_passphrase),
).toBeInstanceOf(OlmMachine);
// Oh, there is 2 databases now, prefixed by `store_name`.
let databases = (await indexedDB.databases()).filter(by_store_name);
expect(databases).toHaveLength(2);
expect(databases).toStrictEqual([
{ name: `${store_name}::matrix-sdk-crypto-meta`, version: 1 },
{ name: `${store_name}::matrix-sdk-crypto`, version: 3 },
]);
// Creating a new Olm machine, with the stored state.
expect(
await OlmMachine.initialize(new UserId("@foo:bar.org"), new DeviceId("baz"), store_name, store_passphrase),
).toBeInstanceOf(OlmMachine);
// Same number of databases.
expect((await indexedDB.databases()).filter(by_store_name)).toHaveLength(2);
});
describe("cannot be instantiated with a store", () => {
test("store name is missing", async () => {
let store_name = null;
let store_passphrase = "world";
let err = null;
try {
await OlmMachine.initialize(
new UserId("@foo:bar.org"),
new DeviceId("baz"),
store_name,
store_passphrase,
);
} catch (error) {
err = error;
}
expect(err).toBeDefined();
});
test("store passphrase is missing", async () => {
let store_name = "hello";
let store_passphrase = null;
let err = null;
try {
await OlmMachine.initialize(
new UserId("@foo:bar.org"),
new DeviceId("baz"),
store_name,
store_passphrase,
);
} catch (error) {
err = error;
}
expect(err).toBeDefined();
});
});
const user = new UserId("@alice:example.org");
const device = new DeviceId("foobar");
const room = new RoomId("!baz:matrix.org");
function machine(new_user, new_device) {
return OlmMachine.initialize(new_user || user, new_device || device);
}
test("can drop/close", async () => {
m = await machine();
m.close();
});
test("can drop/close with a store", async () => {
let store_name = "temporary";
let store_passphrase = "temporary";
const by_store_name = (db) => db.name.startsWith(store_name);
// No databases.
expect((await indexedDB.databases()).filter(by_store_name)).toHaveLength(0);
// Creating a new Olm machine.
const m = await OlmMachine.initialize(
new UserId("@foo:bar.org"),
new DeviceId("baz"),
store_name,
store_passphrase,
);
expect(m).toBeInstanceOf(OlmMachine);
// Oh, there is 2 databases now, prefixed by `store_name`.
let databases = (await indexedDB.databases()).filter(by_store_name);
expect(databases).toHaveLength(2);
expect(databases).toStrictEqual([
{ name: `${store_name}::matrix-sdk-crypto-meta`, version: 1 },
{ name: `${store_name}::matrix-sdk-crypto`, version: 3 },
]);
// Let's force to close the `OlmMachine`.
m.close();
// Now we can delete the databases!
for (const database_name of [`${store_name}::matrix-sdk-crypto`, `${store_name}::matrix-sdk-crypto-meta`]) {
const deleting = indexedDB.deleteDatabase(database_name);
deleting.onsuccess = () => {};
deleting.onerror = () => {
throw new Error("failed to remove the database (error)");
};
deleting.onblocked = () => {
throw new Error("failed to remove the database (blocked)");
};
}
});
test("can read user ID", async () => {
expect((await machine()).userId.toString()).toStrictEqual(user.toString());
});
test("can read device ID", async () => {
expect((await machine()).deviceId.toString()).toStrictEqual(device.toString());
});
test("can read identity keys", async () => {
const identityKeys = (await machine()).identityKeys;
expect(identityKeys.ed25519.toBase64()).toMatch(/^[A-Za-z0-9+/]+$/);
expect(identityKeys.curve25519.toBase64()).toMatch(/^[A-Za-z0-9+/]+$/);
});
test("can read display name", async () => {
expect(await machine().displayName).toBeUndefined();
});
test("can read tracked users", async () => {
const m = await machine();
const trackedUsers = await m.trackedUsers();
expect(trackedUsers).toBeInstanceOf(Set);
expect(trackedUsers.size).toStrictEqual(0);
});
test("can update tracked users", async () => {
const m = await machine();
expect(await m.updateTrackedUsers([user])).toStrictEqual(undefined);
});
test("can receive sync changes", async () => {
const m = await machine();
const toDeviceEvents = JSON.stringify([]);
const changedDevices = new DeviceLists();
const oneTimeKeyCounts = new Map();
const unusedFallbackKeys = new Set();
const receiveSyncChanges = JSON.parse(
await m.receiveSyncChanges(toDeviceEvents, changedDevices, oneTimeKeyCounts, unusedFallbackKeys),
);
expect(receiveSyncChanges).toEqual([]);
});
test("can get the outgoing requests that need to be send out", async () => {
const m = await machine();
const toDeviceEvents = JSON.stringify([]);
const changedDevices = new DeviceLists();
const oneTimeKeyCounts = new Map();
const unusedFallbackKeys = new Set();
const receiveSyncChanges = JSON.parse(
await m.receiveSyncChanges(toDeviceEvents, changedDevices, oneTimeKeyCounts, unusedFallbackKeys),
);
expect(receiveSyncChanges).toEqual([]);
const outgoingRequests = await m.outgoingRequests();
expect(outgoingRequests).toHaveLength(2);
{
expect(outgoingRequests[0]).toBeInstanceOf(KeysUploadRequest);
expect(outgoingRequests[0].id).toBeDefined();
expect(outgoingRequests[0].type).toStrictEqual(RequestType.KeysUpload);
expect(outgoingRequests[0].body).toBeDefined();
const body = JSON.parse(outgoingRequests[0].body);
expect(body.device_keys).toBeDefined();
expect(body.one_time_keys).toBeDefined();
}
{
expect(outgoingRequests[1]).toBeInstanceOf(KeysQueryRequest);
expect(outgoingRequests[1].id).toBeDefined();
expect(outgoingRequests[1].type).toStrictEqual(RequestType.KeysQuery);
expect(outgoingRequests[1].body).toBeDefined();
const body = JSON.parse(outgoingRequests[1].body);
expect(body.timeout).toBeDefined();
expect(body.device_keys).toBeDefined();
expect(body.token).toBeDefined();
}
});
describe("setup workflow to mark requests as sent", () => {
let m;
let ougoingRequests;
beforeAll(async () => {
m = await machine(new UserId("@alice:example.org"), new DeviceId("DEVICEID"));
const toDeviceEvents = JSON.stringify([]);
const changedDevices = new DeviceLists();
const oneTimeKeyCounts = new Map();
const unusedFallbackKeys = new Set();
const receiveSyncChanges = await m.receiveSyncChanges(
toDeviceEvents,
changedDevices,
oneTimeKeyCounts,
unusedFallbackKeys,
);
outgoingRequests = await m.outgoingRequests();
expect(outgoingRequests).toHaveLength(2);
});
test("can mark requests as sent", async () => {
{
const request = outgoingRequests[0];
expect(request).toBeInstanceOf(KeysUploadRequest);
// https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysupload
const hypothetical_response = JSON.stringify({
one_time_key_counts: {
curve25519: 10,
signed_curve25519: 20,
},
});
const marked = await m.markRequestAsSent(request.id, request.type, hypothetical_response);
expect(marked).toStrictEqual(true);
}
{
const request = outgoingRequests[1];
expect(request).toBeInstanceOf(KeysQueryRequest);
// https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysquery
const hypothetical_response = JSON.stringify({
device_keys: {
"@alice:example.org": {
JLAFKJWSCS: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "JLAFKJWSCS",
keys: {
"curve25519:JLAFKJWSCS": "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4",
"ed25519:JLAFKJWSCS": "nE6W2fCblxDcOFmeEtCHNl8/l8bXcu7GKyAswA4r3mM",
},
signatures: {
"@alice:example.org": {
"ed25519:JLAFKJWSCS":
"m53Wkbh2HXkc3vFApZvCrfXcX3AI51GsDHustMhKwlv3TuOJMj4wistcOTM8q2+e/Ro7rWFUb9ZfnNbwptSUBA",
},
},
unsigned: {
device_display_name: "Alice's mobile phone",
},
user_id: "@alice:example.org",
},
},
},
failures: {},
});
const marked = await m.markRequestAsSent(request.id, request.type, hypothetical_response);
expect(marked).toStrictEqual(true);
}
});
});
describe("setup workflow to encrypt/decrypt events", () => {
let m;
const user = new UserId("@alice:example.org");
const device = new DeviceId("JLAFKJWSCS");
const room = new RoomId("!test:localhost");
beforeAll(async () => {
m = await machine(user, device);
});
test("can pass keysquery and keysclaim requests directly", async () => {
{
// derived from https://github.com/matrix-org/matrix-rust-sdk/blob/7f49618d350fab66b7e1dc4eaf64ec25ceafd658/benchmarks/benches/crypto_bench/keys_query.json
const hypothetical_response = JSON.stringify({
device_keys: {
"@example:localhost": {
AFGUOBTZWM: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "AFGUOBTZWM",
keys: {
"curve25519:AFGUOBTZWM": "boYjDpaC+7NkECQEeMh5dC+I1+AfriX0VXG2UV7EUQo",
"ed25519:AFGUOBTZWM": "NayrMQ33ObqMRqz6R9GosmHdT6HQ6b/RX/3QlZ2yiec",
},
signatures: {
"@example:localhost": {
"ed25519:AFGUOBTZWM":
"RoSWvru1jj6fs2arnTedWsyIyBmKHMdOu7r9gDi0BZ61h9SbCK2zLXzuJ9ZFLao2VvA0yEd7CASCmDHDLYpXCA",
},
},
user_id: "@example:localhost",
unsigned: {
device_display_name: "rust-sdk",
},
},
},
},
failures: {},
master_keys: {
"@example:localhost": {
user_id: "@example:localhost",
usage: ["master"],
keys: {
"ed25519:n2lpJGx0LiKnuNE1IucZP3QExrD4SeRP0veBHPe3XUU":
"n2lpJGx0LiKnuNE1IucZP3QExrD4SeRP0veBHPe3XUU",
},
signatures: {
"@example:localhost": {
"ed25519:TCSJXPWGVS":
"+j9G3L41I1fe0++wwusTTQvbboYW0yDtRWUEujhwZz4MAltjLSfJvY0hxhnz+wHHmuEXvQDen39XOpr1p29sAg",
},
},
},
},
self_signing_keys: {
"@example:localhost": {
user_id: "@example:localhost",
usage: ["self_signing"],
keys: {
"ed25519:kQXOuy639Yt47mvNTdrIluoC6DMvfbZLYbxAmwiDyhI":
"kQXOuy639Yt47mvNTdrIluoC6DMvfbZLYbxAmwiDyhI",
},
signatures: {
"@example:localhost": {
"ed25519:n2lpJGx0LiKnuNE1IucZP3QExrD4SeRP0veBHPe3XUU":
"q32ifix/qyRpvmegw2BEJklwoBCAJldDNkcX+fp+lBA4Rpyqtycxge6BA4hcJdxYsy3oV0IHRuugS8rJMMFyAA",
},
},
},
},
user_signing_keys: {
"@example:localhost": {
user_id: "@example:localhost",
usage: ["user_signing"],
keys: {
"ed25519:g4ED07Fnqf3GzVWNN1pZ0IFrPQVdqQf+PYoJNH4eE0s":
"g4ED07Fnqf3GzVWNN1pZ0IFrPQVdqQf+PYoJNH4eE0s",
},
signatures: {
"@example:localhost": {
"ed25519:n2lpJGx0LiKnuNE1IucZP3QExrD4SeRP0veBHPe3XUU":
"nKQu8alQKDefNbZz9luYPcNj+Z+ouQSot4fU/A23ELl1xrI06QVBku/SmDx0sIW1ytso0Cqwy1a+3PzCa1XABg",
},
},
},
},
});
const marked = await m.markRequestAsSent("foo", RequestType.KeysQuery, hypothetical_response);
}
{
// derived from https://github.com/matrix-org/matrix-rust-sdk/blob/7f49618d350fab66b7e1dc4eaf64ec25ceafd658/benchmarks/benches/crypto_bench/keys_claim.json
const hypothetical_response = JSON.stringify({
one_time_keys: {
"@example:localhost": {
AFGUOBTZWM: {
"signed_curve25519:AAAABQ": {
key: "9IGouMnkB6c6HOd4xUsNv4i3Dulb4IS96TzDordzOws",
signatures: {
"@example:localhost": {
"ed25519:AFGUOBTZWM":
"2bvUbbmJegrV0eVP/vcJKuIWC3kud+V8+C0dZtg4dVovOSJdTP/iF36tQn2bh5+rb9xLlSeztXBdhy4c+LiOAg",
},
},
},
},
},
},
failures: {},
});
const marked = await m.markRequestAsSent("bar", RequestType.KeysClaim, hypothetical_response);
}
});
test("can share a room key", async () => {
const other_users = [new UserId("@example:localhost")];
const requests = await m.shareRoomKey(room, other_users, new EncryptionSettings());
expect(requests).toHaveLength(1);
expect(requests[0]).toBeInstanceOf(ToDeviceRequest);
expect(requests[0].event_type).toEqual("m.room.encrypted");
expect(requests[0].txn_id).toBeDefined();
const content = JSON.parse(requests[0].body);
expect(Object.keys(content.messages)).toEqual(["@example:localhost"]);
});
let encrypted;
test("can encrypt an event", async () => {
encrypted = JSON.parse(
await m.encryptRoomEvent(
room,
"m.room.message",
JSON.stringify({
msgtype: "m.text",
body: "Hello, World!",
}),
),
);
expect(encrypted.algorithm).toBeDefined();
expect(encrypted.ciphertext).toBeDefined();
expect(encrypted.sender_key).toBeDefined();
expect(encrypted.device_id).toStrictEqual(device.toString());
expect(encrypted.session_id).toBeDefined();
});
test("can decrypt an event", async () => {
const decrypted = await m.decryptRoomEvent(
JSON.stringify({
type: "m.room.encrypted",
event_id: "$xxxxx:example.org",
origin_server_ts: Date.now(),
sender: user.toString(),
content: encrypted,
unsigned: {
age: 1234,
},
}),
room,
);
expect(decrypted).toBeInstanceOf(DecryptedRoomEvent);
const event = JSON.parse(decrypted.event);
expect(event.content.msgtype).toStrictEqual("m.text");
expect(event.content.body).toStrictEqual("Hello, World!");
expect(decrypted.sender.toString()).toStrictEqual(user.toString());
expect(decrypted.senderDevice.toString()).toStrictEqual(device.toString());
expect(decrypted.senderCurve25519Key).toBeDefined();
expect(decrypted.senderClaimedEd25519Key).toBeDefined();
expect(decrypted.forwardingCurve25519KeyChain).toHaveLength(0);
expect(decrypted.shieldState(true).color).toStrictEqual(ShieldColor.Red);
expect(decrypted.shieldState(false).color).toStrictEqual(ShieldColor.Red);
});
});
test("failure to decrypt returns a valid error", async () => {
const m = await machine();
const evt = {
type: "m.room.encrypted",
event_id: "$xxxxx:example.org",
origin_server_ts: Date.now(),
sender: user.toString(),
content: {
algorithm: "m.megolm.v1.aes-sha2",
ciphertext: "blah",
},
};
await expect(() => m.decryptRoomEvent(JSON.stringify(evt), room)).rejects.toThrowError();
});
test("can read cross-signing status", async () => {
const m = await machine();
const crossSigningStatus = await m.crossSigningStatus();
expect(crossSigningStatus).toBeInstanceOf(CrossSigningStatus);
expect(crossSigningStatus.hasMaster).toStrictEqual(false);
expect(crossSigningStatus.hasSelfSigning).toStrictEqual(false);
expect(crossSigningStatus.hasUserSigning).toStrictEqual(false);
});
test("can sign a message", async () => {
const m = await machine();
const signatures = await m.sign("foo");
expect(signatures.isEmpty()).toStrictEqual(false);
expect(signatures.count).toStrictEqual(1);
let base64;
// `get`
{
const signature = signatures.get(user);
expect(signature.has("ed25519:foobar")).toStrictEqual(true);
const s = signature.get("ed25519:foobar");
expect(s).toBeInstanceOf(MaybeSignature);
expect(s.isValid()).toStrictEqual(true);
expect(s.isInvalid()).toStrictEqual(false);
expect(s.invalidSignatureSource).toBeUndefined();
base64 = s.signature.toBase64();
expect(base64).toMatch(/^[A-Za-z0-9\+/]+$/);
expect(s.signature.ed25519.toBase64()).toStrictEqual(base64);
}
// `getSignature`
{
const signature = signatures.getSignature(user, new DeviceKeyId("ed25519:foobar"));
expect(signature.toBase64()).toStrictEqual(base64);
}
// Unknown signatures.
{
expect(signatures.get(new UserId("@hello:example.org"))).toBeUndefined();
expect(signatures.getSignature(user, new DeviceKeyId("world:foobar"))).toBeUndefined();
}
});
test("can get a user identities", async () => {
const m = await machine();
let _ = m.bootstrapCrossSigning(true);
const identity = await m.getIdentity(user);
expect(identity).toBeInstanceOf(OwnUserIdentity);
const signatureUploadRequest = await identity.verify();
expect(signatureUploadRequest).toBeInstanceOf(SignatureUploadRequest);
const [verificationRequest, outgoingVerificationRequest] = await identity.requestVerification();
expect(verificationRequest).toBeInstanceOf(VerificationRequest);
expect(outgoingVerificationRequest).toBeInstanceOf(ToDeviceRequest);
const isTrusted = await identity.trustsOurOwnDevice();
expect(isTrusted).toStrictEqual(false);
});
describe("can export/import room keys", () => {
let exportedRoomKeys;
test("can export room keys", async () => {
let m = await machine();
await m.shareRoomKey(room, [new UserId("@bob:example.org")], new EncryptionSettings());
exportedRoomKeys = await m.exportRoomKeys((session) => {
expect(session).toBeInstanceOf(InboundGroupSession);
expect(session.senderKey.toBase64()).toEqual(m.identityKeys.curve25519.toBase64());
expect(session.roomId.toString()).toStrictEqual(room.toString());
expect(session.sessionId).toBeDefined();
expect(session.hasBeenImported()).toStrictEqual(false);
return true;
});
const roomKeys = JSON.parse(exportedRoomKeys);
expect(roomKeys).toHaveLength(1);
expect(roomKeys[0]).toMatchObject({
algorithm: expect.any(String),
room_id: room.toString(),
sender_key: expect.any(String),
session_id: expect.any(String),
session_key: expect.any(String),
sender_claimed_keys: {
ed25519: expect.any(String),
},
forwarding_curve25519_key_chain: [],
});
});
let encryptedExportedRoomKeys;
let encryptionPassphrase = "Hello, Matrix!";
test("can encrypt the exported room keys", () => {
encryptedExportedRoomKeys = OlmMachine.encryptExportedRoomKeys(
exportedRoomKeys,
encryptionPassphrase,
100_000,
);
expect(encryptedExportedRoomKeys).toMatch(/^-----BEGIN MEGOLM SESSION DATA-----/);
});
test("can decrypt the exported room keys", () => {
const decryptedExportedRoomKeys = OlmMachine.decryptExportedRoomKeys(
encryptedExportedRoomKeys,
encryptionPassphrase,
);
expect(decryptedExportedRoomKeys).toStrictEqual(exportedRoomKeys);
});
test("can import room keys", async () => {
const progressListener = (progress, total) => {
expect(progress).toBeLessThan(total);
// Since it's called only once, let's be crazy.
expect(progress).toStrictEqual(0n);
expect(total).toStrictEqual(1n);
};
let m = await machine();
const result = JSON.parse(await m.importRoomKeys(exportedRoomKeys, progressListener));
expect(result).toMatchObject({
imported_count: expect.any(Number),
total_count: expect.any(Number),
keys: expect.any(Object),
});
});
test("importing room keys calls RoomKeyUpdatedCallback", async () => {
const callback = jest.fn();
callback.mockImplementation(() => Promise.resolve(undefined));
let m = await machine();
m.registerRoomKeyUpdatedCallback(callback);
await m.importRoomKeys(exportedRoomKeys, (_, _1) => {});
expect(callback).toHaveBeenCalledTimes(1);
let keyInfoList = callback.mock.calls[0][0];
expect(keyInfoList.length).toEqual(1);
expect(keyInfoList[0].roomId.toString()).toStrictEqual(room.toString());
});
});
describe("can do in-room verification", () => {
let m;
const user = new UserId("@alice:example.org");
const device = new DeviceId("JLAFKJWSCS");
const room = new RoomId("!test:localhost");
beforeAll(async () => {
m = await machine(user, device);
});
test("can inject devices from someone else", async () => {
{
const hypothetical_response = JSON.stringify({
device_keys: {
"@example:morpheus.localhost": {
ATRLDCRXAC: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "ATRLDCRXAC",
keys: {
"curve25519:ATRLDCRXAC": "cAVT5Es3Z3F5pFD+2w3HT7O9+R3PstzYVkzD51X/FWQ",
"ed25519:ATRLDCRXAC": "V2w/T/x7i7AXiCCtS6JldrpbvRliRoef3CqTUNqMRHA",
},
signatures: {
"@example:morpheus.localhost": {
"ed25519:ATRLDCRXAC":
"ro2BjO5J6089B/JOANHnFmGrogrC2TIdMlgJbJO00DjOOcGxXfvOezCFIORTwZNHvkHU617YIGl/4keTDIWvBQ",
},
},
user_id: "@example:morpheus.localhost",
unsigned: {
device_display_name: "Element Desktop: Linux",
},
},
EYYGYTCTNC: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "EYYGYTCTNC",
keys: {
"curve25519:EYYGYTCTNC": "Pqu50fo472wgb6NjKkaUxjuqoAIEAmhln2gw/zSQ7Ek",
"ed25519:EYYGYTCTNC": "Pf/2QPvui8lDty6TCTglVPRVM+irNHYavNNkyv5yFpU",
},
signatures: {
"@example:morpheus.localhost": {
"ed25519:EYYGYTCTNC":
"pnP5BYLEUUaxDgrvdzCznkjNDbvY1/MFBr1JejdnLiXlcmxRULQpIWZUCO7QTbULsCwMsYQNGn50nfmjBQX3CQ",
},
},
user_id: "@example:morpheus.localhost",
unsigned: {
device_display_name: "WeeChat-Matrix-rs",
},
},
SUMODVLSIU: {
algorithms: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
device_id: "SUMODVLSIU",
keys: {
"curve25519:SUMODVLSIU": "geQXWGWc++gcUHk0JcFmEVSjyzDOnk2mjVsUQwbNqQU",
"ed25519:SUMODVLSIU": "ccktaQ3g+B18E6FwVhTBYie26OlHbvDUzDEtxOQ4Qcs",
},
signatures: {
"@example:morpheus.localhost": {
"ed25519:SUMODVLSIU":
"Yn+AOxHRt1GQpY2xT2Jcqqn8jh5+Vw23ctA7NXyDiWPsLPLNTpjGWHMjZdpUqflQvpiKfhODPICoIa7Pu0iSAg",
"ed25519:rUiMNDjIu6gqsrhJPbj3phyIzuEtuQGrLOEa9mCbtTM":
"Cio6k/sq289XNTOvTCWre7Q6zg+A3euzMUe7Uy1T3gPqYFzX+kt7EAxrhbPqx1HyXAEz9zD0D/uw9VEXFCvWBQ",
},
},
user_id: "@example:morpheus.localhost",
unsigned: {
device_display_name: "Element Desktop (Linux)",
},
},
},
},
failures: {},
master_keys: {
"@example:morpheus.localhost": {
user_id: "@example:morpheus.localhost",
usage: ["master"],
keys: {
"ed25519:ZzU4WCyBfOFitdGmfKCq6F39iQCDk/zhNNTsi+tWH7A":
"ZzU4WCyBfOFitdGmfKCq6F39iQCDk/zhNNTsi+tWH7A",
},
signatures: {
"@example:morpheus.localhost": {
"ed25519:SUMODVLSIU":
"RL6WOuuzB/mZ+edfUFG/KeEcmKh+NaWpM6m2bUYmDnJrtTCYyoU+pgHJuL2/6nynemmONo18JEHBuqtNcMq2AQ",
},
},
},
},
self_signing_keys: {
"@example:morpheus.localhost": {
user_id: "@example:morpheus.localhost",
usage: ["self_signing"],
keys: {
"ed25519:rUiMNDjIu6gqsrhJPbj3phyIzuEtuQGrLOEa9mCbtTM":
"rUiMNDjIu6gqsrhJPbj3phyIzuEtuQGrLOEa9mCbtTM",
},
signatures: {
"@example:morpheus.localhost": {
"ed25519:ZzU4WCyBfOFitdGmfKCq6F39iQCDk/zhNNTsi+tWH7A":
"uCBn9rpeg6umY8H97ejN26UMp6QDwNL98869t1DoVGL50J8adLN05OZd8lYk9QzwTr2d56ZTGYSYX8kv28SDDA",
},
},
},
},
user_signing_keys: {
"@example:morpheus.localhost": {
user_id: "@example:morpheus.localhost",
usage: ["user_signing"],
keys: {
"ed25519:GLhEKLQ50jnF6IMEPsO2ucpHUNIUEnbBXs5gYbHg4Aw":
"GLhEKLQ50jnF6IMEPsO2ucpHUNIUEnbBXs5gYbHg4Aw",
},
signatures: {
"@example:morpheus.localhost": {
"ed25519:ZzU4WCyBfOFitdGmfKCq6F39iQCDk/zhNNTsi+tWH7A":
"4fIyWlVzuz1pgoegNLZASycORXqKycVS0dNq5vmmwsVEudp1yrPhndnaIJ3fjF8LDHvwzXTvohOid7DiU1j0AA",
},
},
},
},
});
const marked = await m.markRequestAsSent("foo", RequestType.KeysQuery, hypothetical_response);
}
});
test("can start an in-room SAS verification", async () => {
let _ = m.bootstrapCrossSigning(true);
const identity = await m.getIdentity(new UserId("@example:morpheus.localhost"));
expect(identity).toBeInstanceOf(UserIdentity);
expect(identity.isVerified()).toStrictEqual(false);
const eventId = new EventId("$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg");
const verificationRequest = await identity.requestVerification(room, eventId);
expect(verificationRequest).toBeInstanceOf(VerificationRequest);
await m.receiveVerificationEvent(
JSON.stringify({
sender: "@example:morpheus.localhost",
type: "m.key.verification.ready",
event_id: "$QguWmaeMt6Hao7Ea6XHDInvr8ndknev79t9a2eBxlz0",
origin_server_ts: 1674037263075,
content: {
"methods": ["m.sas.v1", "m.qr_code.show.v1", "m.reciprocate.v1"],
"from_device": "SUMODVLSIU",
"m.relates_to": {
rel_type: "m.reference",
event_id: eventId.toString(),
},
},
}),
room,
);
expect(verificationRequest.roomId.toString()).toStrictEqual(room.toString());
const [_sas, outgoingVerificationRequest] = await verificationRequest.startSas();
expect(outgoingVerificationRequest).toBeInstanceOf(RoomMessageRequest);
expect(outgoingVerificationRequest.id).toBeDefined();
expect(outgoingVerificationRequest.room_id).toStrictEqual(room.toString());
expect(outgoingVerificationRequest.txn_id).toBeDefined();
expect(outgoingVerificationRequest.event_type).toStrictEqual("m.key.verification.start");
expect(outgoingVerificationRequest.body).toBeDefined();
const body = JSON.parse(outgoingVerificationRequest.body);
expect(body).toMatchObject({
"from_device": expect.any(String),
"method": "m.sas.v1",
"key_agreement_protocols": [expect.any(String)],
"hashes": [expect.any(String)],
"message_authentication_codes": [expect.any(String), expect.any(String)],
"short_authentication_string": ["decimal", "emoji"],
"m.relates_to": {
rel_type: "m.reference",
event_id: eventId.toString(),
},
});
});
});
});
@@ -0,0 +1,22 @@
const {
RequestType,
KeysUploadRequest,
KeysQueryRequest,
KeysClaimRequest,
ToDeviceRequest,
SignatureUploadRequest,
RoomMessageRequest,
KeysBackupRequest,
} = require("../pkg/matrix_sdk_crypto_js");
describe("RequestType", () => {
test("has the correct variant values", () => {
expect(RequestType.KeysUpload).toStrictEqual(0);
expect(RequestType.KeysQuery).toStrictEqual(1);
expect(RequestType.KeysClaim).toStrictEqual(2);
expect(RequestType.ToDevice).toStrictEqual(3);
expect(RequestType.SignatureUpload).toStrictEqual(4);
expect(RequestType.RoomMessage).toStrictEqual(5);
expect(RequestType.KeysBackup).toStrictEqual(6);
});
});
@@ -0,0 +1,31 @@
const { DeviceLists, UserId } = require("../pkg/matrix_sdk_crypto_js");
describe(DeviceLists.name, () => {
test("can be empty", () => {
const empty = new DeviceLists();
expect(empty.isEmpty()).toStrictEqual(true);
expect(empty.changed).toHaveLength(0);
expect(empty.left).toHaveLength(0);
});
test("can be coerced empty", () => {
const empty = new DeviceLists([], []);
expect(empty.isEmpty()).toStrictEqual(true);
expect(empty.changed).toHaveLength(0);
expect(empty.left).toHaveLength(0);
});
test("returns the correct `changed` and `left`", () => {
const list = new DeviceLists([new UserId("@foo:bar.org")], [new UserId("@baz:qux.org")]);
expect(list.isEmpty()).toStrictEqual(false);
expect(list.changed).toHaveLength(1);
expect(list.changed[0].toString()).toStrictEqual("@foo:bar.org");
expect(list.left).toHaveLength(1);
expect(list.left[0].toString()).toStrictEqual("@baz:qux.org");
});
});
@@ -0,0 +1,91 @@
const { Tracing, LoggerLevel, OlmMachine, UserId, DeviceId } = require("../pkg/matrix_sdk_crypto_js");
describe("LoggerLevel", () => {
test("has the correct variant values", () => {
expect(LoggerLevel.Trace).toStrictEqual(0);
expect(LoggerLevel.Debug).toStrictEqual(1);
expect(LoggerLevel.Info).toStrictEqual(2);
expect(LoggerLevel.Warn).toStrictEqual(3);
expect(LoggerLevel.Error).toStrictEqual(4);
});
});
describe(Tracing.name, () => {
if (Tracing.isAvailable()) {
let tracing = new Tracing(LoggerLevel.Debug);
test("can installed several times", () => {
new Tracing(LoggerLevel.Debug);
new Tracing(LoggerLevel.Warn);
new Tracing(LoggerLevel.Debug);
});
const originalConsoleDebug = console.debug;
for (const [testName, testPreState, testPostState, expectedGotcha] of [
["can log something", () => {}, () => {}, true],
[
"can change the logger level",
() => {
tracing.minLevel = LoggerLevel.Warn;
},
() => {
tracing.minLevel = LoggerLevel.Debug;
},
false,
],
[
"can be turned off",
() => {
tracing.turnOff();
},
() => {},
false,
],
[
"can be turned on",
() => {
tracing.turnOn();
},
() => {},
true,
],
// This one *must* be the last. We are turning tracing off
// again for the other tests.
[
"can be turned off",
() => {
tracing.turnOff();
},
() => {},
false,
],
]) {
test(testName, async () => {
testPreState();
let gotcha = false;
console.debug = (msg) => {
gotcha = true;
expect(msg).not.toHaveLength(0);
};
// Do something that emits a `DEBUG` log.
await OlmMachine.initialize(new UserId("@alice:example.org"), new DeviceId("foo"));
console.debug = originalConsoleDebug;
testPostState();
expect(gotcha).toStrictEqual(expectedGotcha);
});
}
} else {
test("cannot be constructed", () => {
expect(() => {
new Tracing(LoggerLevel.Error);
}).toThrow();
});
}
});
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"strict": true
},
"typedocOptions": {
"entryPoints": ["pkg/matrix_sdk_crypto_js.d.ts"],
"out": "docs",
"readme": "README.md"
}
}
@@ -0,0 +1,7 @@
/node_modules
/package-lock.json
/index.js
/index.d.ts
/matrix-sdk-crypto.*.node
/docs/*
*.tgz

Some files were not shown because too many files have changed in this diff Show More