You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-31 15:24:23 +03:00
ElementR: Add rust-crypto#createRecoveryKeyFromPassphrase
implementation (#3472)
* Add `rust-crypto#createRecoveryKeyFromPassphrase` implementation * Use `crypto` * Rename `IRecoveryKey` into `GeneratedSecretStorageKey` for rust crypto * Improve comments * Improve `createRecoveryKeyFromPassphrase`
This commit is contained in:
@ -356,6 +356,39 @@ describe("RustCrypto", () => {
|
||||
expect(res).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("createRecoveryKeyFromPassphrase", () => {
|
||||
let rustCrypto: RustCrypto;
|
||||
|
||||
beforeEach(async () => {
|
||||
rustCrypto = await makeTestRustCrypto();
|
||||
});
|
||||
|
||||
it("should create a recovery key without password", async () => {
|
||||
const recoveryKey = await rustCrypto.createRecoveryKeyFromPassphrase();
|
||||
|
||||
// Expected the encoded private key to have 59 chars
|
||||
expect(recoveryKey.encodedPrivateKey?.length).toBe(59);
|
||||
// Expect the private key to be an Uint8Array with a length of 32
|
||||
expect(recoveryKey.privateKey).toBeInstanceOf(Uint8Array);
|
||||
expect(recoveryKey.privateKey.length).toBe(32);
|
||||
// Expect keyInfo to be empty
|
||||
expect(Object.keys(recoveryKey.keyInfo!).length).toBe(0);
|
||||
});
|
||||
|
||||
it("should create a recovery key with password", async () => {
|
||||
const recoveryKey = await rustCrypto.createRecoveryKeyFromPassphrase("my password");
|
||||
|
||||
// Expected the encoded private key to have 59 chars
|
||||
expect(recoveryKey.encodedPrivateKey?.length).toBe(59);
|
||||
// Expect the private key to be an Uint8Array with a length of 32
|
||||
expect(recoveryKey.privateKey).toBeInstanceOf(Uint8Array);
|
||||
expect(recoveryKey.privateKey.length).toBe(32);
|
||||
// Expect keyInfo.passphrase to be filled
|
||||
expect(recoveryKey.keyInfo?.passphrase?.algorithm).toBe("m.pbkdf2");
|
||||
expect(recoveryKey.keyInfo?.passphrase?.iterations).toBe(500000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/** build a basic RustCrypto instance for testing
|
||||
|
@ -18,6 +18,7 @@ import type { IMegolmSessionData } from "./@types/crypto";
|
||||
import { Room } from "./models/room";
|
||||
import { DeviceMap } from "./models/device";
|
||||
import { UIAuthCallback } from "./interactive-auth";
|
||||
import { AddSecretStorageKeyOpts } from "./secret-storage";
|
||||
|
||||
/** Types of cross-signing key */
|
||||
export enum CrossSigningKey {
|
||||
@ -26,6 +27,17 @@ export enum CrossSigningKey {
|
||||
UserSigning = "user_signing",
|
||||
}
|
||||
|
||||
/**
|
||||
* Recovery key created by {@link CryptoApi#createRecoveryKeyFromPassphrase}
|
||||
*/
|
||||
export interface GeneratedSecretStorageKey {
|
||||
keyInfo?: AddSecretStorageKeyOpts;
|
||||
/** The raw generated private key. */
|
||||
privateKey: Uint8Array;
|
||||
/** The generated key, encoded for display to the user per https://spec.matrix.org/v1.7/client-server-api/#key-representation. */
|
||||
encodedPrivateKey?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public interface to the cryptography parts of the js-sdk
|
||||
*
|
||||
@ -201,6 +213,20 @@ export interface CryptoApi {
|
||||
* @returns The current status of cross-signing keys: whether we have public and private keys cached locally, and whether the private keys are in secret storage.
|
||||
*/
|
||||
getCrossSigningStatus(): Promise<CrossSigningStatus>;
|
||||
|
||||
/**
|
||||
* Create a recovery key (ie, a key suitable for use with server-side secret storage).
|
||||
*
|
||||
* The key can either be based on a user-supplied passphrase, or just created randomly.
|
||||
*
|
||||
* @param password - Optional passphrase string to use to derive the key,
|
||||
* which can later be entered by the user as an alternative to entering the
|
||||
* recovery key itself. If omitted, a key is generated randomly.
|
||||
*
|
||||
* @returns Object including recovery key and server upload parameters.
|
||||
* The private key should be disposed of after displaying to the use.
|
||||
*/
|
||||
createRecoveryKeyFromPassphrase(password?: string): Promise<GeneratedSecretStorageKey>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,10 +16,10 @@ limitations under the License.
|
||||
|
||||
import { DeviceInfo } from "./deviceinfo";
|
||||
import { IKeyBackupInfo } from "./keybackup";
|
||||
import type { AddSecretStorageKeyOpts } from "../secret-storage";
|
||||
import { GeneratedSecretStorageKey } from "../crypto-api";
|
||||
|
||||
/* re-exports for backwards compatibility. */
|
||||
export { CrossSigningKey } from "../crypto-api";
|
||||
export { CrossSigningKey, GeneratedSecretStorageKey as IRecoveryKey } from "../crypto-api";
|
||||
|
||||
export type {
|
||||
ImportRoomKeyProgressData as IImportOpts,
|
||||
@ -66,12 +66,6 @@ export interface IEncryptedEventInfo {
|
||||
mismatchedSender: boolean;
|
||||
}
|
||||
|
||||
export interface IRecoveryKey {
|
||||
keyInfo?: AddSecretStorageKeyOpts;
|
||||
privateKey: Uint8Array;
|
||||
encodedPrivateKey?: string;
|
||||
}
|
||||
|
||||
export interface ICreateSecretStorageOpts {
|
||||
/**
|
||||
* Function called to await a secret storage key creation flow.
|
||||
@ -79,7 +73,7 @@ export interface ICreateSecretStorageOpts {
|
||||
* recovery key which should be disposed of after displaying to the user,
|
||||
* and raw private key to avoid round tripping if needed.
|
||||
*/
|
||||
createSecretStorageKey?: () => Promise<IRecoveryKey>;
|
||||
createSecretStorageKey?: () => Promise<GeneratedSecretStorageKey>;
|
||||
|
||||
/**
|
||||
* The current key backup object. If passed,
|
||||
|
@ -34,16 +34,20 @@ import {
|
||||
BootstrapCrossSigningOpts,
|
||||
CrossSigningStatus,
|
||||
DeviceVerificationStatus,
|
||||
GeneratedSecretStorageKey,
|
||||
ImportRoomKeyProgressData,
|
||||
ImportRoomKeysOpts,
|
||||
CrossSigningKey,
|
||||
} from "../crypto-api";
|
||||
import { deviceKeysToDeviceMap, rustDeviceToJsDevice } from "./device-converter";
|
||||
import { IDownloadKeyResult, IQueryKeysRequest } from "../client";
|
||||
import { Device, DeviceMap } from "../models/device";
|
||||
import { ServerSideSecretStorage } from "../secret-storage";
|
||||
import { CrossSigningKey } from "../crypto/api";
|
||||
import { AddSecretStorageKeyOpts, ServerSideSecretStorage } from "../secret-storage";
|
||||
import { CrossSigningIdentity } from "./CrossSigningIdentity";
|
||||
import { secretStorageContainsCrossSigningKeys } from "./secret-storage";
|
||||
import { keyFromPassphrase } from "../crypto/key_passphrase";
|
||||
import { encodeRecoveryKey } from "../crypto/recoverykey";
|
||||
import { crypto } from "../crypto/crypto";
|
||||
|
||||
/**
|
||||
* An implementation of {@link CryptoBackend} using the Rust matrix-sdk-crypto.
|
||||
@ -405,6 +409,36 @@ export class RustCrypto implements CryptoBackend {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#createRecoveryKeyFromPassphrase}
|
||||
*/
|
||||
public async createRecoveryKeyFromPassphrase(password?: string): Promise<GeneratedSecretStorageKey> {
|
||||
let key: Uint8Array;
|
||||
|
||||
const keyInfo: AddSecretStorageKeyOpts = {};
|
||||
if (password) {
|
||||
// Generate the key from the passphrase
|
||||
const derivation = await keyFromPassphrase(password);
|
||||
keyInfo.passphrase = {
|
||||
algorithm: "m.pbkdf2",
|
||||
iterations: derivation.iterations,
|
||||
salt: derivation.salt,
|
||||
};
|
||||
key = derivation.key;
|
||||
} else {
|
||||
// Using the navigator crypto API to generate the private key
|
||||
key = new Uint8Array(32);
|
||||
crypto.getRandomValues(key);
|
||||
}
|
||||
|
||||
const encodedPrivateKey = encodeRecoveryKey(key);
|
||||
return {
|
||||
keyInfo,
|
||||
encodedPrivateKey,
|
||||
privateKey: key,
|
||||
};
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SyncCryptoCallbacks implementation
|
||||
|
Reference in New Issue
Block a user