Skip to content

Commit

Permalink
Add crypto methods for export and import of secrets bundle (#4227)
Browse files Browse the repository at this point in the history
* Add crypto methods for OIDC QR code login

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Improve test

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Revert test due to hang inside Rust.

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update test name

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update test name

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
  • Loading branch information
t3chguy authored Jun 5, 2024
1 parent c88487d commit a3cea8c
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 0 deletions.
38 changes: 38 additions & 0 deletions spec/unit/rust-crypto/rust-crypto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1515,6 +1515,44 @@ describe("RustCrypto", () => {
expect(await rustCrypto.isDehydrationSupported()).toBe(true);
});
});

describe("import & export secrets bundle", () => {
let rustCrypto: RustCrypto;

beforeEach(async () => {
rustCrypto = await makeTestRustCrypto(
new MatrixHttpApi(new TypedEventEmitter<HttpApiEvent, HttpApiEventHandlerMap>(), {
baseUrl: "http://server/",
prefix: "",
onlyData: true,
}),
testData.TEST_USER_ID,
);
});

it("should throw an error if there is nothing to export", async () => {
await expect(rustCrypto.exportsSecretsBundle()).rejects.toThrow(
"The store doesn't contain any cross-signing keys",
);
});

it("should correctly import & export a secrets bundle", async () => {
const bundle = {
cross_signing: {
master_key: "bMnVpkHI4S2wXRxy+IpaKM5PIAUUkl6DE+n0YLIW/qs",
user_signing_key: "8tlgLjUrrb/zGJo4YKGhDTIDCEjtJTAS/Sh2AGNLuIo",
self_signing_key: "pfDknmP5a0fVVRE54zhkUgJfzbNmvKcNfIWEW796bQs",
},
backup: {
algorithm: "m.megolm_backup.v1.curve25519-aes-sha2",
key: "bYYv3aFLQ49jMNcOjuTtBY9EKDby2x1m3gfX81nIKRQ",
backup_version: "9",
},
};
await rustCrypto.importSecretsBundle(bundle);
await expect(rustCrypto.exportsSecretsBundle()).resolves.toEqual(expect.objectContaining(bundle));
});
});
});

/** Build a MatrixHttpApi instance */
Expand Down
40 changes: 40 additions & 0 deletions src/@types/matrix-sdk-crypto-wasm.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Copyright 2024 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.
*/

import type * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";

declare module "@matrix-org/matrix-sdk-crypto-wasm" {
interface OlmMachine {
importSecretsBundle(bundle: RustSdkCryptoJs.SecretsBundle): Promise<void>;
exportSecretsBundle(): Promise<RustSdkCryptoJs.SecretsBundle>;
}

interface SecretsBundle {
// eslint-disable-next-line @typescript-eslint/naming-convention
to_json(): Promise<{
cross_signing: {
master_key: string;
self_signing_key: string;
user_signing_key: string;
};
backup?: {
algorithm: string;
key: string;
backup_version: string;
};
}>;
}
}
18 changes: 18 additions & 0 deletions src/crypto-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import type { SecretsBundle } from "@matrix-org/matrix-sdk-crypto-wasm";
import type { IMegolmSessionData } from "./@types/crypto";
import { Room } from "./models/room";
import { DeviceMap } from "./models/device";
Expand Down Expand Up @@ -532,6 +533,23 @@ export interface CryptoApi {
* to false.
*/
startDehydration(createNewKey?: boolean): Promise<void>;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Import/export of secret keys
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
* Export secrets bundle for transmitting to another device as part of OIDC QR login
*/
exportSecretsBundle?(): Promise<Awaited<ReturnType<SecretsBundle["to_json"]>>>;

/**
* Import secrets bundle transmitted from another device.
* @param secrets - The secrets bundle received from the other device
*/
importSecretsBundle?(secrets: Awaited<ReturnType<SecretsBundle["to_json"]>>): Promise<void>;
}

/** A reason code for a failure to decrypt an event. */
Expand Down
21 changes: 21 additions & 0 deletions src/rust-crypto/rust-crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
CrossSigningKey,
CrossSigningKeyInfo,
CrossSigningStatus,
CryptoApi,
CryptoCallbacks,
Curve25519AuthData,
DecryptionFailureCode,
Expand Down Expand Up @@ -1165,6 +1166,26 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
this.checkKeyBackupAndEnable();
}

/**
* Implementation of {@link CryptoApi#importSecretsBundle}.
*/
public async importSecretsBundle(
secrets: Parameters<NonNullable<CryptoApi["importSecretsBundle"]>>[0],
): Promise<void> {
const secretsBundle = RustSdkCryptoJs.SecretsBundle.from_json(secrets);
await this.getOlmMachineOrThrow().importSecretsBundle(secretsBundle); // this method frees the SecretsBundle
}

/**
* Implementation of {@link CryptoApi#exportSecretsBundle}.
*/
public async exportsSecretsBundle(): ReturnType<NonNullable<CryptoApi["exportSecretsBundle"]>> {
const secretsBundle = await this.getOlmMachineOrThrow().exportSecretsBundle();
const secrets = secretsBundle.to_json();
secretsBundle.free();
return secrets;
}

/**
* Signs the given object with the current device and current identity (if available).
* As defined in {@link https://spec.matrix.org/v1.8/appendices/#signing-json | Signing JSON}.
Expand Down

0 comments on commit a3cea8c

Please sign in to comment.