Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update e2e doc in README.md #4503

Merged
merged 9 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 124 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,44 +303,142 @@ Then visit `http://localhost:8005` to see the API docs.

# End-to-end encryption support

**This section is outdated.** Use of `libolm` is deprecated and we are replacing it with support
from the matrix-rust-sdk (https://github.com/element-hq/element-web/issues/21972).
`matrix-js-sdk`'s end-to-end encryption support is based on the [WebAssembly bindings](https://github.com/matrix-org/matrix-rust-sdk-crypto-wasm) of the Rust [matrix-sdk-crypto](https://github.com/matrix-org/matrix-rust-sdk/tree/main/crates/matrix-sdk-crypto) library.

The SDK supports end-to-end encryption via the Olm and Megolm protocols, using
[libolm](https://gitlab.matrix.org/matrix-org/olm). It is left up to the
application to make libolm available, via the `Olm` global.
## Initialization

It is also necessary to call `await matrixClient.initCrypto()` after creating a new
`MatrixClient` (but **before** calling `matrixClient.startClient()`) to
initialise the crypto layer.
**Do not use `matrixClient.initCrypto()`. This method is deprecated and no longer maintained.**

If the `Olm` global is not available, the SDK will show a warning, as shown
below; `initCrypto()` will also fail.
To initialize the end-to-end encryption support in the matrix client:

```javascript
// Create a new matrix client
const matrixClient = sdk.createClient({
baseUrl: "http://localhost:8008",
accessToken: myAccessToken,
userId: myUserId,
});

// Initialize to enable end-to-end encryption support.
await matrixClient.initRustCrypto();
```

### Use the `CryptoApi`

The [`CryptoApi`](https://matrix-org.github.io/matrix-js-sdk/interfaces/crypto_api.CryptoApi.html) interface is the main entry point for end-to-end encryption.

To obtain a reference, call [`MatrixClient.getCrypto`](https://matrix-org.github.io/matrix-js-sdk/classes/matrix.MatrixClient.html#getCrypto).
florianduros marked this conversation as resolved.
Show resolved Hide resolved

### Secret storage

You should set up the [secret storage](https://spec.matrix.org/v1.12/client-server-api/#secret-storage) before using the end-to-end encryption. To do this, you need to call [`CryptoApi.bootstrapSecretStorage`](https://matrix-org.github.io/matrix-js-sdk/interfaces/crypto_api.CryptoApi.html#bootstrapSecretStorage).
florianduros marked this conversation as resolved.
Show resolved Hide resolved
`CryptoApi.bootstrapSecretStorage` can be called unconditionally, but it will only set up the secret storage if it is not already set up (unless you use the `setupNewSecretStorage` parameter).
florianduros marked this conversation as resolved.
Show resolved Hide resolved

```javascript
const matrixClient = sdk.createClient({
...,
cryptoCallbacks: {
getSecretStorageKey: (keys) => {
florianduros marked this conversation as resolved.
Show resolved Hide resolved
// This function should return the secret storage keys returned in `bootstrapSecretStorage#createSecretStorageKey`
florianduros marked this conversation as resolved.
Show resolved Hide resolved
return mySecretStorageKeys;
},
},
});

matrixClient.getCrypto().bootstrapSecretStorage({
// This will reset the secret storage if it is already set up.
// If you want to keep the current secret storage, you can set `setupNewSecretStorage` to `false`.
// If `setupNewSecretStorage` is `true`, you need to fill `createSecretStorageKey`
setupNewSecretStorage: true,
florianduros marked this conversation as resolved.
Show resolved Hide resolved
// This function will be called if a new secret storage key (aka recovery key) is needed.
// You should prompt the user to save the key somewhere, because you will need it to unlock the secret storage.
florianduros marked this conversation as resolved.
Show resolved Hide resolved
createSecretStorageKey: async () => {
return mySecretStorageKey;
},
});
```

In the example above, we are setting up a new secret storage. The secret storage data will be encrypted using the secret storage key returned in [`createSecretStorageKey`](https://matrix-org.github.io/matrix-js-sdk/interfaces/crypto_api.CreateSecretStorageOpts.html#createSecretStorageKey).
florianduros marked this conversation as resolved.
Show resolved Hide resolved
We recommend that you prompt the user to re-enter this key when [`CryptoCallbacks.getSecretStorageKey`](https://matrix-org.github.io/matrix-js-sdk/interfaces/crypto_api.CryptoCallbacks.html#getSecretStorageKey) is called (when the secret storage access is needed).

Also, if you don't have a [key backup](https://spec.matrix.org/v1.12/client-server-api/#server-side-key-backups) you should create one:
florianduros marked this conversation as resolved.
Show resolved Hide resolved
florianduros marked this conversation as resolved.
Show resolved Hide resolved

```javascript
// Check if we have a key backup.
// checkKeyBackupAndEnable returns null, there is no key backup.
const hasKeyBackup = await matrixClient.getCrypto().checkKeyBackupAndEnable() !== null
florianduros marked this conversation as resolved.
Show resolved Hide resolved

// First option when setting up the secret storage
matrixClient.getCrypto().bootstrapSecretStorage({
...,
setupNewKeyBackup: !hasKeyBackup,
});

// Second option
florianduros marked this conversation as resolved.
Show resolved Hide resolved
matrixClient.getCrypto().resetKeyBackup();
florianduros marked this conversation as resolved.
Show resolved Hide resolved
```
Unable to load crypto module: crypto will be disabled: Error: global.Olm is not defined

Once the key backup and the secret storage are set up, you don't need to set them up again for all your devices.
florianduros marked this conversation as resolved.
Show resolved Hide resolved

### Set up cross-signing
florianduros marked this conversation as resolved.
Show resolved Hide resolved

To set up cross-signing to verify devices and other users, call [`CryptoApi.bootstrapCrossSigning`](https://matrix-org.github.io/matrix-js-sdk/interfaces/crypto_api.CryptoApi.html#bootstrapCrossSigning):

```javascript
matrixClient.getCrypto().bootstrapCrossSigning({
authUploadDeviceSigningKeys: async (makeRequest) => {
return makeRequest(authDict);
},
});
```

If the crypto layer is not (successfully) initialised, the SDK will continue to
work for unencrypted rooms, but it will not support the E2E parts of the Matrix
specification.
The [`authUploadDeviceSigningKeys`](https://matrix-org.github.io/matrix-js-sdk/interfaces/crypto_api.BootstrapCrossSigningOpts.html#authUploadDeviceSigningKeys) callback
is required in order to upload newly-generated public cross-signing keys to the server.

### Verify a new device

Once the cross-signing is set up on one of your devices, you can verify another device with two methods:

1. Use `CryptoApi.bootstrapCrossSigning`
florianduros marked this conversation as resolved.
Show resolved Hide resolved

`bootstrapCrossSigning` will call the [CryptoCallbacks.getSecretStorageKey](https://matrix-org.github.io/matrix-js-sdk/interfaces/crypto_api.CryptoCallbacks.html#getSecretStorageKey) callback. The device is verified with the private cross-signing keys fetched from the secret storage.
florianduros marked this conversation as resolved.
Show resolved Hide resolved

2. Request an interactive verification against existing devices, by calling [CryptoApi.requestOwnUserVerification](https://matrix-org.github.io/matrix-js-sdk/interfaces/crypto_api.CryptoApi.html#requestOwnUserVerification).

## Migrating from the legacy crypto stack to Rust crypto

To provide the Olm library in a browser application:
If your application previously used the legacy crypto stack, (i.e, it called `MatrixClient.initCrypto()`), you will
need to migrate existing devices to the Rust crypto stack.

- download the transpiled libolm (from https://packages.matrix.org/npm/olm/).
- load `olm.js` as a `<script>` _before_ `browser-matrix.js`.
This migration happens automatically when you call `initRustCrypto()` instead of `initCrypto()`,
but you need to provide the legacy [`cryptoStore`](https://matrix-org.github.io/matrix-js-sdk/interfaces/matrix.ICreateClientOpts.html#cryptoStore) and [`pickleKey`](https://matrix-org.github.io/matrix-js-sdk/interfaces/matrix.ICreateClientOpts.html#pickleKey) to [`createClient`](https://matrix-org.github.io/matrix-js-sdk/functions/matrix.createClient.html):

To provide the Olm library in a node.js application:
```javascript
// You should provide the legacy crypto store and the pickle key to the matrix client in order to migrate the data.
const matrixClient = sdk.createClient({
cryptoStore: myCryptoStore,
pickleKey: myPickleKey,
baseUrl: "http://localhost:8008",
accessToken: myAccessToken,
userId: myUserId,
});

// The migration will be done automatically when you call `initRustCrypto`.
await matrixClient.initRustCrypto();
```

To follow the migration progress, you can listen to the `CryptoEvent.LegacyCryptoStoreMigrationProgress` event:
florianduros marked this conversation as resolved.
Show resolved Hide resolved

```javascript
// When progress === total === -1, the migration is finished.
matrixClient.on(CryptoEvent.LegacyCryptoStoreMigrationProgress, (progress, total) => {
...
});
```

- `yarn add https://packages.matrix.org/npm/olm/olm-3.1.4.tgz`
(replace the URL with the latest version you want to use from
https://packages.matrix.org/npm/olm/)
- `global.Olm = require('olm');` _before_ loading `matrix-js-sdk`.
After the migration is finished, you can remove the legacy crypto store and the pickle key from the matrix client creation.

florianduros marked this conversation as resolved.
Show resolved Hide resolved
florianduros marked this conversation as resolved.
Show resolved Hide resolved
If you want to package Olm as dependency for your node.js application, you can
use `yarn add https://packages.matrix.org/npm/olm/olm-3.1.4.tgz`. If your
application also works without e2e crypto enabled, add `--optional` to mark it
as an optional dependency.
The Rust crypto is not supported in a lot of deprecated methods of [`MatrixClient`](https://matrix-org.github.io/matrix-js-sdk/classes/matrix.MatrixClient.html). If you use them, you should migrate to the [`CryptoApi`](https://matrix-org.github.io/matrix-js-sdk/interfaces/crypto_api.CryptoApi.html). Also, the legacy `MatrixClient.crypto` object is not available anymore, you should use `MatrixClient.getCrypto()` instead.
florianduros marked this conversation as resolved.
Show resolved Hide resolved

# Contributing

Expand Down
11 changes: 8 additions & 3 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,12 +286,16 @@ export interface ICreateClientOpts {
store?: Store;

/**
* A store to be used for end-to-end crypto session data. If not specified,
* end-to-end crypto will be disabled. The `createClient` helper will create
* a default store if needed. Calls the factory supplied to
* A store to be used for end-to-end crypto session data.
* The `createClient` helper will create a default store if needed. Calls the factory supplied to
* {@link setCryptoStoreFactory} if unspecified; or if no factory has been
* specified, uses a default implementation (indexeddb in the browser,
* in-memory otherwise).
*
* This is only used for the legacy crypto implementation (as used by {@link MatrixClient#initCrypto}),
* but if you use the rust crypto implementation ({@link MatrixClient#initRustCrypto}) and the device
* previously used legacy crypto (so must be migrated), then this must still be provided, so that the
* data can be migrated from the legacy store.
florianduros marked this conversation as resolved.
Show resolved Hide resolved
*/
cryptoStore?: CryptoStore;

Expand Down Expand Up @@ -1269,6 +1273,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
protected isGuestAccount = false;
protected ongoingScrollbacks: { [roomId: string]: { promise?: Promise<Room>; errorTs?: number } } = {};
protected notifTimelineSet: EventTimelineSet | null = null;
/* @deprecated */
protected cryptoStore?: CryptoStore;
protected verificationMethods?: string[];
protected fallbackICEServerAllowed = false;
Expand Down