You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-09 10:22:46 +03:00
Fix rust migration when ssss secret not encrypted (#4168)
This commit is contained in:
@@ -304,6 +304,56 @@ describe("initRustCrypto", () => {
|
|||||||
}
|
}
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
||||||
|
it("migrates data from a legacy crypto store when secret are not encrypted", async () => {
|
||||||
|
const PICKLE_KEY = "pickle1234";
|
||||||
|
const legacyStore = new MemoryCryptoStore();
|
||||||
|
|
||||||
|
// It's possible for old sessions to directly store the secrets as raw UInt8Array,
|
||||||
|
// so we need to support that in the migration code.
|
||||||
|
// See https://github.com/matrix-org/matrix-js-sdk/commit/c81f11df0afd4d0da3b088892745ae2f8ba1c4a7
|
||||||
|
async function storeSecretKeyInClear(type: string, key: Uint8Array, store: CryptoStore) {
|
||||||
|
// @ts-ignore The API to store raw UInt8Array does not exist anymore, so we need that for this test.
|
||||||
|
store.privateKeys[type as keyof SecretStorePrivateKeys] = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the legacy store with some test data
|
||||||
|
const storeSecretKey = (type: string, key: string) =>
|
||||||
|
storeSecretKeyInClear(type, new TextEncoder().encode(key), legacyStore);
|
||||||
|
|
||||||
|
await legacyStore.storeAccount({}, "not a real account");
|
||||||
|
await storeSecretKey("master", "master key");
|
||||||
|
await storeSecretKey("self_signing", "ssk");
|
||||||
|
await storeSecretKey("user_signing", "usk");
|
||||||
|
|
||||||
|
fetchMock.get("path:/_matrix/client/v3/room_keys/version", 404);
|
||||||
|
|
||||||
|
function legacyMigrationProgressListener(progress: number, total: number): void {
|
||||||
|
logger.log(`migrated ${progress} of ${total}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await initRustCrypto({
|
||||||
|
logger,
|
||||||
|
http: makeMatrixHttpApi(),
|
||||||
|
userId: TEST_USER,
|
||||||
|
deviceId: TEST_DEVICE_ID,
|
||||||
|
secretStorage: {} as ServerSideSecretStorage,
|
||||||
|
cryptoCallbacks: {} as CryptoCallbacks,
|
||||||
|
storePrefix: "storePrefix",
|
||||||
|
storePassphrase: "storePassphrase",
|
||||||
|
legacyCryptoStore: legacyStore,
|
||||||
|
legacyPickleKey: PICKLE_KEY,
|
||||||
|
legacyMigrationProgressListener,
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = mocked(Migration.migrateBaseData).mock.calls[0][0];
|
||||||
|
expect(data.pickledAccount).toEqual("not a real account");
|
||||||
|
expect(data.userId!.toString()).toEqual(TEST_USER);
|
||||||
|
expect(data.deviceId!.toString()).toEqual(TEST_DEVICE_ID);
|
||||||
|
expect(atob(data.privateCrossSigningMasterKey!)).toEqual("master key");
|
||||||
|
expect(atob(data.privateCrossSigningUserSigningKey!)).toEqual("usk");
|
||||||
|
expect(atob(data.privateCrossSigningSelfSigningKey!)).toEqual("ssk");
|
||||||
|
});
|
||||||
|
|
||||||
it("handles megolm sessions with no `keysClaimed`", async () => {
|
it("handles megolm sessions with no `keysClaimed`", async () => {
|
||||||
const legacyStore = new MemoryCryptoStore();
|
const legacyStore = new MemoryCryptoStore();
|
||||||
legacyStore.storeAccount({}, "not a real account");
|
legacyStore.storeAccount({}, "not a real account");
|
||||||
|
@@ -27,6 +27,7 @@ import { CrossSigningKeyInfo, Curve25519AuthData } from "../crypto-api";
|
|||||||
import { RustCrypto } from "./rust-crypto";
|
import { RustCrypto } from "./rust-crypto";
|
||||||
import { KeyBackupInfo } from "../crypto-api/keybackup";
|
import { KeyBackupInfo } from "../crypto-api/keybackup";
|
||||||
import { sleep } from "../utils";
|
import { sleep } from "../utils";
|
||||||
|
import { encodeBase64 } from "../base64";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if any data needs migrating from the legacy store, and do so.
|
* Determine if any data needs migrating from the legacy store, and do so.
|
||||||
@@ -400,19 +401,20 @@ async function getAndDecryptCachedSecretKey(
|
|||||||
legacyPickleKey: Uint8Array,
|
legacyPickleKey: Uint8Array,
|
||||||
name: string,
|
name: string,
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
let encodedKey: IEncryptedPayload | null = null;
|
const key = await new Promise<any>((resolve) => {
|
||||||
|
legacyStore.doTxn("readonly", [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
|
||||||
await legacyStore.doTxn("readonly", "account", (txn) => {
|
legacyStore.getSecretStorePrivateKey(txn, resolve, name as keyof SecretStorePrivateKeys);
|
||||||
legacyStore.getSecretStorePrivateKey(
|
});
|
||||||
txn,
|
|
||||||
(k) => {
|
|
||||||
encodedKey = k as IEncryptedPayload | null;
|
|
||||||
},
|
|
||||||
name as keyof SecretStorePrivateKeys,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return encodedKey === null ? undefined : await decryptAES(encodedKey, legacyPickleKey, name);
|
if (key && key.ciphertext && key.iv && key.mac) {
|
||||||
|
return await decryptAES(key as IEncryptedPayload, legacyPickleKey, name);
|
||||||
|
} else if (key instanceof Uint8Array) {
|
||||||
|
// This is a legacy backward compatibility case where the key was stored in clear.
|
||||||
|
return encodeBase64(key);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user