Skip to content

Commit

Permalink
Fix rust migration when ssss secret not encrypted (#4168)
Browse files Browse the repository at this point in the history
  • Loading branch information
BillCarsonFr authored Apr 26, 2024
1 parent 1da5e8f commit 65d858f
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 11 deletions.
50 changes: 50 additions & 0 deletions spec/unit/rust-crypto/rust-crypto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,56 @@ describe("initRustCrypto", () => {
}
}, 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 () => {
const legacyStore = new MemoryCryptoStore();
legacyStore.storeAccount({}, "not a real account");
Expand Down
24 changes: 13 additions & 11 deletions src/rust-crypto/libolm_migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { CrossSigningKeyInfo, Curve25519AuthData } from "../crypto-api";
import { RustCrypto } from "./rust-crypto";
import { KeyBackupInfo } from "../crypto-api/keybackup";
import { sleep } from "../utils";
import { encodeBase64 } from "../base64";

/**
* Determine if any data needs migrating from the legacy store, and do so.
Expand Down Expand Up @@ -400,19 +401,20 @@ async function getAndDecryptCachedSecretKey(
legacyPickleKey: Uint8Array,
name: string,
): Promise<string | undefined> {
let encodedKey: IEncryptedPayload | null = null;

await legacyStore.doTxn("readonly", "account", (txn) => {
legacyStore.getSecretStorePrivateKey(
txn,
(k) => {
encodedKey = k as IEncryptedPayload | null;
},
name as keyof SecretStorePrivateKeys,
);
const key = await new Promise<any>((resolve) => {
legacyStore.doTxn("readonly", [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
legacyStore.getSecretStorePrivateKey(txn, resolve, 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;
}
}

/**
Expand Down

0 comments on commit 65d858f

Please sign in to comment.