Element-R: pass pickleKey in as raw key for indexeddb encryption (#12543)
* Element-R: pass pickleKey in as raw key for indexeddb encryption Currently, we pass the `pickleKey` to the rust library for use as a passphrase for encrypting its crypto store. The Rust libary then passes that passphrase through 200000 rounds of PBKDF2 to generate an encryption key, which is (deliberately) slow. However, the pickleKey is actually 32 bytes of random data (base64-encoded). By passing the raw key into the rust library, we can therefore save the PBKDF operation. Backwards-compatibility with existing sessions is maintained, because if the rust library discovers that the store was previously encrypted with a key based on a PBKDF, it will re-base64 and PBKDF the key we provide, thus reconstructing the right key. * Update src/Lifecycle.ts Co-authored-by: Florian Duros <florianduros@element.io> * Lifecycle-test: clean up test setup Rely less on the unit under test for setting up the test preconditions -- not least because we don't really want to fire up matrix clients and the like during test setup. * Factor out "encryptPickleKey" method For a start it makes it easier to grok what's going on, but also I went to use this in a test * Improve tests for `Lifecycle.restoreFromLocalStorage` --------- Co-authored-by: Florian Duros <florianduros@element.io>
This commit is contained in:
parent
5004456d82
commit
0a01320fca
6 changed files with 233 additions and 64 deletions
|
@ -66,6 +66,27 @@ export interface IMatrixClientCreds {
|
|||
freshLogin?: boolean;
|
||||
}
|
||||
|
||||
export interface MatrixClientPegAssignOpts {
|
||||
/**
|
||||
* If we are using Rust crypto, a key with which to encrypt the indexeddb.
|
||||
*
|
||||
* If provided, it must be exactly 32 bytes of data. If both this and
|
||||
* {@link MatrixClientPegAssignOpts.rustCryptoStorePassword} are undefined,
|
||||
* the store will be unencrypted.
|
||||
*/
|
||||
rustCryptoStoreKey?: Uint8Array;
|
||||
|
||||
/**
|
||||
* If we are using Rust crypto, a password which will be used to derive a key to encrypt the store with.
|
||||
*
|
||||
* An alternative to {@link MatrixClientPegAssignOpts.rustCryptoStoreKey}. Ignored if `rustCryptoStoreKey` is set.
|
||||
*
|
||||
* Deriving a key from a password is (deliberately) a slow operation, so prefer to pass a `rustCryptoStoreKey`
|
||||
* directly where possible.
|
||||
*/
|
||||
rustCryptoStorePassword?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the current instance of the `MatrixClient` to use across the codebase.
|
||||
* Looking for an `MatrixClient`? Just look for the `MatrixClientPeg` on the peg
|
||||
|
@ -94,14 +115,14 @@ export interface IMatrixClientPeg {
|
|||
unset(): void;
|
||||
|
||||
/**
|
||||
* Prepare the MatrixClient for use, including initialising the store and crypto, but do not start it
|
||||
* Prepare the MatrixClient for use, including initialising the store and crypto, but do not start it.
|
||||
*/
|
||||
assign(): Promise<IStartClientOpts>;
|
||||
assign(opts?: MatrixClientPegAssignOpts): Promise<IStartClientOpts>;
|
||||
|
||||
/**
|
||||
* Prepare the MatrixClient for use, including initialising the store and crypto, and start it
|
||||
* Prepare the MatrixClient for use, including initialising the store and crypto, and start it.
|
||||
*/
|
||||
start(): Promise<void>;
|
||||
start(opts?: MatrixClientPegAssignOpts): Promise<void>;
|
||||
|
||||
/**
|
||||
* If we've registered a user ID we set this to the ID of the
|
||||
|
@ -248,7 +269,10 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
|||
PlatformPeg.get()?.reload();
|
||||
};
|
||||
|
||||
public async assign(): Promise<IStartClientOpts> {
|
||||
/**
|
||||
* Implementation of {@link IMatrixClientPeg.assign}.
|
||||
*/
|
||||
public async assign(assignOpts: MatrixClientPegAssignOpts = {}): Promise<IStartClientOpts> {
|
||||
if (!this.matrixClient) {
|
||||
throw new Error("createClient must be called first");
|
||||
}
|
||||
|
@ -275,7 +299,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
|||
|
||||
// try to initialise e2e on the new client
|
||||
if (!SettingsStore.getValue("lowBandwidth")) {
|
||||
await this.initClientCrypto();
|
||||
await this.initClientCrypto(assignOpts.rustCryptoStoreKey, assignOpts.rustCryptoStorePassword);
|
||||
}
|
||||
|
||||
const opts = utils.deepCopy(this.opts);
|
||||
|
@ -301,8 +325,16 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
|||
|
||||
/**
|
||||
* Attempt to initialize the crypto layer on a newly-created MatrixClient
|
||||
*
|
||||
* @param rustCryptoStoreKey - If we are using Rust crypto, a key with which to encrypt the indexeddb.
|
||||
* If provided, it must be exactly 32 bytes of data. If both this and `rustCryptoStorePassword` are
|
||||
* undefined, the store will be unencrypted.
|
||||
*
|
||||
* @param rustCryptoStorePassword - An alternative to `rustCryptoStoreKey`. Ignored if `rustCryptoStoreKey` is set.
|
||||
* A password which will be used to derive a key to encrypt the store with. Deriving a key from a password is
|
||||
* (deliberately) a slow operation, so prefer to pass a `rustCryptoStoreKey` directly where possible.
|
||||
*/
|
||||
private async initClientCrypto(): Promise<void> {
|
||||
private async initClientCrypto(rustCryptoStoreKey?: Uint8Array, rustCryptoStorePassword?: string): Promise<void> {
|
||||
if (!this.matrixClient) {
|
||||
throw new Error("createClient must be called first");
|
||||
}
|
||||
|
@ -338,7 +370,13 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
|||
|
||||
// Now we can initialise the right crypto impl.
|
||||
if (useRustCrypto) {
|
||||
await this.matrixClient.initRustCrypto();
|
||||
if (!rustCryptoStoreKey && !rustCryptoStorePassword) {
|
||||
logger.error("Warning! Not using an encryption key for rust crypto store.");
|
||||
}
|
||||
await this.matrixClient.initRustCrypto({
|
||||
storageKey: rustCryptoStoreKey,
|
||||
storagePassword: rustCryptoStorePassword,
|
||||
});
|
||||
|
||||
StorageManager.setCryptoInitialised(true);
|
||||
// TODO: device dehydration and whathaveyou
|
||||
|
@ -367,8 +405,11 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
|||
}
|
||||
}
|
||||
|
||||
public async start(): Promise<void> {
|
||||
const opts = await this.assign();
|
||||
/**
|
||||
* Implementation of {@link IMatrixClientPeg.start}.
|
||||
*/
|
||||
public async start(assignOpts?: MatrixClientPegAssignOpts): Promise<void> {
|
||||
const opts = await this.assign(assignOpts);
|
||||
|
||||
logger.log(`MatrixClientPeg: really starting MatrixClient`);
|
||||
await this.matrixClient!.startClient(opts);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue