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);
|
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
|
/** build a basic RustCrypto instance for testing
|
||||||
|
@ -18,6 +18,7 @@ import type { IMegolmSessionData } from "./@types/crypto";
|
|||||||
import { Room } from "./models/room";
|
import { Room } from "./models/room";
|
||||||
import { DeviceMap } from "./models/device";
|
import { DeviceMap } from "./models/device";
|
||||||
import { UIAuthCallback } from "./interactive-auth";
|
import { UIAuthCallback } from "./interactive-auth";
|
||||||
|
import { AddSecretStorageKeyOpts } from "./secret-storage";
|
||||||
|
|
||||||
/** Types of cross-signing key */
|
/** Types of cross-signing key */
|
||||||
export enum CrossSigningKey {
|
export enum CrossSigningKey {
|
||||||
@ -26,6 +27,17 @@ export enum CrossSigningKey {
|
|||||||
UserSigning = "user_signing",
|
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
|
* 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.
|
* @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>;
|
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 { DeviceInfo } from "./deviceinfo";
|
||||||
import { IKeyBackupInfo } from "./keybackup";
|
import { IKeyBackupInfo } from "./keybackup";
|
||||||
import type { AddSecretStorageKeyOpts } from "../secret-storage";
|
import { GeneratedSecretStorageKey } from "../crypto-api";
|
||||||
|
|
||||||
/* re-exports for backwards compatibility. */
|
/* re-exports for backwards compatibility. */
|
||||||
export { CrossSigningKey } from "../crypto-api";
|
export { CrossSigningKey, GeneratedSecretStorageKey as IRecoveryKey } from "../crypto-api";
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
ImportRoomKeyProgressData as IImportOpts,
|
ImportRoomKeyProgressData as IImportOpts,
|
||||||
@ -66,12 +66,6 @@ export interface IEncryptedEventInfo {
|
|||||||
mismatchedSender: boolean;
|
mismatchedSender: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRecoveryKey {
|
|
||||||
keyInfo?: AddSecretStorageKeyOpts;
|
|
||||||
privateKey: Uint8Array;
|
|
||||||
encodedPrivateKey?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ICreateSecretStorageOpts {
|
export interface ICreateSecretStorageOpts {
|
||||||
/**
|
/**
|
||||||
* Function called to await a secret storage key creation flow.
|
* 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,
|
* recovery key which should be disposed of after displaying to the user,
|
||||||
* and raw private key to avoid round tripping if needed.
|
* and raw private key to avoid round tripping if needed.
|
||||||
*/
|
*/
|
||||||
createSecretStorageKey?: () => Promise<IRecoveryKey>;
|
createSecretStorageKey?: () => Promise<GeneratedSecretStorageKey>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current key backup object. If passed,
|
* The current key backup object. If passed,
|
||||||
|
@ -34,16 +34,20 @@ import {
|
|||||||
BootstrapCrossSigningOpts,
|
BootstrapCrossSigningOpts,
|
||||||
CrossSigningStatus,
|
CrossSigningStatus,
|
||||||
DeviceVerificationStatus,
|
DeviceVerificationStatus,
|
||||||
|
GeneratedSecretStorageKey,
|
||||||
ImportRoomKeyProgressData,
|
ImportRoomKeyProgressData,
|
||||||
ImportRoomKeysOpts,
|
ImportRoomKeysOpts,
|
||||||
|
CrossSigningKey,
|
||||||
} from "../crypto-api";
|
} from "../crypto-api";
|
||||||
import { deviceKeysToDeviceMap, rustDeviceToJsDevice } from "./device-converter";
|
import { deviceKeysToDeviceMap, rustDeviceToJsDevice } from "./device-converter";
|
||||||
import { IDownloadKeyResult, IQueryKeysRequest } from "../client";
|
import { IDownloadKeyResult, IQueryKeysRequest } from "../client";
|
||||||
import { Device, DeviceMap } from "../models/device";
|
import { Device, DeviceMap } from "../models/device";
|
||||||
import { ServerSideSecretStorage } from "../secret-storage";
|
import { AddSecretStorageKeyOpts, ServerSideSecretStorage } from "../secret-storage";
|
||||||
import { CrossSigningKey } from "../crypto/api";
|
|
||||||
import { CrossSigningIdentity } from "./CrossSigningIdentity";
|
import { CrossSigningIdentity } from "./CrossSigningIdentity";
|
||||||
import { secretStorageContainsCrossSigningKeys } from "./secret-storage";
|
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.
|
* 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
|
// SyncCryptoCallbacks implementation
|
||||||
|
Reference in New Issue
Block a user