Skip to content

Commit

Permalink
Element-R: implement {get,store}SessionBackupPrivateKey (#3622)
Browse files Browse the repository at this point in the history
  • Loading branch information
richvdh authored Jul 26, 2023
1 parent 29b815b commit 0e95df5
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 10 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
],
"dependencies": {
"@babel/runtime": "^7.12.5",
"@matrix-org/matrix-sdk-crypto-wasm": "^1.0.1",
"@matrix-org/matrix-sdk-crypto-wasm": "^1.1.0",
"another-json": "^0.2.0",
"bs58": "^5.0.0",
"content-type": "^1.0.4",
Expand Down
10 changes: 10 additions & 0 deletions spec/unit/rust-crypto/rust-crypto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,16 @@ describe("RustCrypto", () => {
);
});
});

describe("get|storeSessionBackupPrivateKey", () => {
it("can save and restore a key", async () => {
const key = "testtesttesttesttesttesttesttest";
const rustCrypto = await makeTestRustCrypto();
await rustCrypto.storeSessionBackupPrivateKey(new TextEncoder().encode(key));
const fetched = await rustCrypto.getSessionBackupPrivateKey();
expect(new TextDecoder().decode(fetched!)).toEqual(key);
});
});
});

/** build a basic RustCrypto instance for testing
Expand Down
14 changes: 9 additions & 5 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3726,10 +3726,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
backupInfo: IKeyBackupInfo,
opts?: IKeyBackupRestoreOpts,
): Promise<IKeyBackupRestoreResult> {
if (!this.crypto) {
if (!this.cryptoBackend) {
throw new Error("End-to-end encryption disabled");
}
const privKey = await this.crypto.getSessionBackupPrivateKey();
const privKey = await this.cryptoBackend.getSessionBackupPrivateKey();
if (!privKey) {
throw new Error("Couldn't get key");
}
Expand Down Expand Up @@ -3767,7 +3767,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
const cacheCompleteCallback = opts?.cacheCompleteCallback;
const progressCallback = opts?.progressCallback;

if (!this.crypto) {
if (!this.cryptoBackend) {
throw new Error("End-to-end encryption disabled");
}

Expand All @@ -3790,9 +3790,13 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
return Promise.reject(new MatrixError({ errcode: MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY }));
}

if (!(privKey instanceof Uint8Array)) {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
throw new Error(`restoreKeyBackup expects Uint8Array, got ${privKey}`);
}
// Cache the key, if possible.
// This is async.
this.crypto
this.cryptoBackend
.storeSessionBackupPrivateKey(privKey)
.catch((e) => {
logger.warn("Error caching session backup key:", e);
Expand Down Expand Up @@ -3849,7 +3853,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
algorithm.free();
}

await this.getCrypto()?.importRoomKeys(keys, {
await this.cryptoBackend.importRoomKeys(keys, {
progressCallback,
untrusted,
source: "backup",
Expand Down
24 changes: 24 additions & 0 deletions src/crypto-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,30 @@ export interface CryptoApi {
* @returns a VerificationRequest when the request has been sent to the other party.
*/
requestDeviceVerification(userId: string, deviceId: string): Promise<VerificationRequest>;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Secure key backup
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
* Fetch the backup decryption key we have saved in our store.
*
* This can be used for gossiping the key to other devices.
*
* @returns the key, if any, or null
*/
getSessionBackupPrivateKey(): Promise<Uint8Array | null>;

/**
* Store the backup decryption key.
*
* This should be called if the client has received the key from another device via secret sharing (gossiping).
*
* @param key - the backup decryption key
*/
storeSessionBackupPrivateKey(key: Uint8Array): Promise<void>;
}

/**
Expand Down
27 changes: 27 additions & 0 deletions src/rust-crypto/rust-crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,33 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
return new RustVerificationRequest(request, this.outgoingRequestProcessor, this._supportedVerificationMethods);
}

/**
* Fetch the backup decryption key we have saved in our store.
*
* Implementation of {@link CryptoApi#getSessionBackupPrivateKey}.
*
* @returns the key, if any, or null
*/
public async getSessionBackupPrivateKey(): Promise<Uint8Array | null> {
const backupKeys: RustSdkCryptoJs.BackupKeys = await this.olmMachine.getBackupKeys();
if (!backupKeys.decryptionKeyBase64) return null;
return Buffer.from(backupKeys.decryptionKeyBase64, "base64");
}

/**
* Store the backup decryption key.
*
* Implementation of {@link CryptoApi#storeSessionBackupPrivateKey}.
*
* @param key - the backup decryption key
*/
public async storeSessionBackupPrivateKey(key: Uint8Array): Promise<void> {
const base64Key = Buffer.from(key).toString("base64");

// TODO get version from backupManager
await this.olmMachine.saveBackupDecryptionKey(RustSdkCryptoJs.BackupDecryptionKey.fromBase64(base64Key), "");
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SyncCryptoCallbacks implementation
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1482,10 +1482,10 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"

"@matrix-org/matrix-sdk-crypto-wasm@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-1.0.1.tgz#21a0557a7bb3f60b37c6d412be8906c056fe79b8"
integrity sha512-VTwV5IowvhhLXwAsDDAv02bC5/qBQbG2YtpYAije11253sQ3MePIoSR+dS40Ih3lAlEzqQ00GU3O+i45jMzIRQ==
"@matrix-org/matrix-sdk-crypto-wasm@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-1.1.0.tgz#43996a2c5fc8786999eeaaf6df51007244f6b3c4"
integrity sha512-BSMYqXRgQOHG3N18z8b05x3UQcdLL3XDrxjtjjA88t9PadZ7RwNowLm1Sx3ESzdzRX+r1SEVAWs2JnTTs0rv3Q==

"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz":
version "3.2.14"
Expand Down

0 comments on commit 0e95df5

Please sign in to comment.