163 lines
7.3 KiB
JavaScript
163 lines
7.3 KiB
JavaScript
import _objectSpread from "@babel/runtime/helpers/objectSpread2";
|
|
/*
|
|
Copyright 2022 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 * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
|
import { StoreHandle } from "@matrix-org/matrix-sdk-crypto-wasm";
|
|
import { MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE, RustCrypto } from "./rust-crypto.js";
|
|
import { MigrationState } from "../crypto/store/base.js";
|
|
import { migrateFromLegacyCrypto, migrateLegacyLocalTrustIfNeeded, migrateRoomSettingsFromLegacyCrypto } from "./libolm_migration.js";
|
|
|
|
/**
|
|
* The arguments used to initialise RustCrypto, passed in to initRustCrypto.
|
|
*
|
|
* @internal
|
|
*/
|
|
|
|
/**
|
|
* Create a new `RustCrypto` implementation
|
|
*
|
|
* @param args - InitRustCryptoArgs
|
|
* @internal
|
|
*/
|
|
export async function initRustCrypto(args) {
|
|
const {
|
|
logger
|
|
} = args;
|
|
|
|
// initialise the rust matrix-sdk-crypto-wasm, if it hasn't already been done
|
|
logger.debug("Initialising Rust crypto-sdk WASM artifact");
|
|
await RustSdkCryptoJs.initAsync();
|
|
logger.debug("Opening Rust CryptoStore");
|
|
let storeHandle;
|
|
if (args.storePrefix) {
|
|
if (args.storeKey) {
|
|
storeHandle = await StoreHandle.openWithKey(args.storePrefix, args.storeKey, logger);
|
|
} else {
|
|
storeHandle = await StoreHandle.open(args.storePrefix, args.storePassphrase, logger);
|
|
}
|
|
} else {
|
|
storeHandle = await StoreHandle.open(null, null, logger);
|
|
}
|
|
if (args.legacyCryptoStore) {
|
|
// We have a legacy crypto store, which we may need to migrate from.
|
|
await migrateFromLegacyCrypto(_objectSpread({
|
|
legacyStore: args.legacyCryptoStore,
|
|
storeHandle
|
|
}, args));
|
|
}
|
|
const rustCrypto = await initOlmMachine(args, storeHandle);
|
|
storeHandle.free();
|
|
logger.debug("Completed rust crypto-sdk setup");
|
|
return rustCrypto;
|
|
}
|
|
async function initOlmMachine({
|
|
logger,
|
|
http,
|
|
userId,
|
|
deviceId,
|
|
secretStorage,
|
|
cryptoCallbacks,
|
|
legacyCryptoStore,
|
|
enableEncryptedStateEvents,
|
|
caCertsPem
|
|
}, storeHandle) {
|
|
logger.debug("Init OlmMachine");
|
|
const olmMachine = await RustSdkCryptoJs.OlmMachine.initFromStore(new RustSdkCryptoJs.UserId(userId), new RustSdkCryptoJs.DeviceId(deviceId), storeHandle, logger, caCertsPem);
|
|
|
|
// A final migration step, now that we have an OlmMachine.
|
|
if (legacyCryptoStore) {
|
|
await migrateRoomSettingsFromLegacyCrypto({
|
|
logger,
|
|
legacyStore: legacyCryptoStore,
|
|
olmMachine
|
|
});
|
|
}
|
|
|
|
// Disable room key requests, per https://github.com/vector-im/element-web/issues/26524.
|
|
olmMachine.roomKeyRequestsEnabled = false;
|
|
const rustCrypto = new RustCrypto(logger, olmMachine, http, userId, deviceId, secretStorage, cryptoCallbacks, enableEncryptedStateEvents);
|
|
olmMachine.registerRoomKeyUpdatedCallback(sessions => rustCrypto.onRoomKeysUpdated(sessions));
|
|
olmMachine.registerRoomKeysWithheldCallback(withheld => rustCrypto.onRoomKeysWithheld(withheld));
|
|
olmMachine.registerUserIdentityUpdatedCallback(userId => rustCrypto.onUserIdentityUpdated(userId));
|
|
olmMachine.registerDevicesUpdatedCallback(userIds => rustCrypto.onDevicesUpdated(userIds));
|
|
|
|
// Check if there are any key backup secrets pending processing. There may be multiple secrets to process if several devices have gossiped them.
|
|
// The `registerReceiveSecretCallback` function will only be triggered for new secrets. If the client is restarted before processing them, the secrets will need to be manually handled.
|
|
void rustCrypto.checkSecrets("m.megolm_backup.v1");
|
|
|
|
// Register a callback to be notified when a new secret is received, as for now only the key backup secret is supported (the cross signing secrets are handled automatically by the OlmMachine)
|
|
olmMachine.registerReceiveSecretCallback((name, _value) =>
|
|
// Instead of directly checking the secret value, we poll the inbox to get all values for that secret type.
|
|
// Once we have all the values, we can safely clear the secret inbox.
|
|
rustCrypto.checkSecrets(name));
|
|
|
|
// Tell the OlmMachine to think about its outgoing requests before we hand control back to the application.
|
|
//
|
|
// This is primarily a fudge to get it to correctly populate the `users_for_key_query` list, so that future
|
|
// calls to getIdentity (etc) block until the key queries are performed.
|
|
//
|
|
// Note that we don't actually need to *make* any requests here; it is sufficient to tell the Rust side to think
|
|
// about them.
|
|
//
|
|
// XXX: find a less hacky way to do this.
|
|
await olmMachine.outgoingRequests();
|
|
if (legacyCryptoStore && (await legacyCryptoStore.containsData())) {
|
|
const migrationState = await legacyCryptoStore.getMigrationState();
|
|
if (migrationState < MigrationState.INITIAL_OWN_KEY_QUERY_DONE) {
|
|
logger.debug(`Performing initial key query after migration`);
|
|
// We need to do an initial keys query so that the rust stack can properly update trust of
|
|
// the user device and identity from the migrated private keys.
|
|
// If not done, there is a short period where the own device/identity trust will be undefined after migration.
|
|
let initialKeyQueryDone = false;
|
|
while (!initialKeyQueryDone) {
|
|
try {
|
|
await rustCrypto.userHasCrossSigningKeys(userId);
|
|
initialKeyQueryDone = true;
|
|
} catch (e) {
|
|
// If the initial key query fails, we retry until it succeeds.
|
|
logger.error("Failed to check for cross-signing keys after migration, retrying", e);
|
|
}
|
|
}
|
|
|
|
// If the private master cross-signing key was not cached in the legacy store, the rust session
|
|
// will not be able to establish the trust of the user identity.
|
|
// That means that after migration the session could revert to unverified.
|
|
// In order to avoid asking the users to re-verify their sessions, we need to migrate the legacy local trust
|
|
// (if the legacy session was already verified) to the new session.
|
|
await migrateLegacyLocalTrustIfNeeded({
|
|
legacyCryptoStore,
|
|
rustCrypto,
|
|
logger
|
|
});
|
|
await legacyCryptoStore.setMigrationState(MigrationState.INITIAL_OWN_KEY_QUERY_DONE);
|
|
}
|
|
}
|
|
|
|
// If we have any recently-joined rooms, see if we have a pending key bundle for them.
|
|
for (const pendingDetails of await olmMachine.getAllRoomsPendingKeyBundles()) {
|
|
const roomId = pendingDetails.roomId.toString();
|
|
if (Date.now() - pendingDetails.inviteAcceptedAtMillis <= MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE) {
|
|
logger.info(`Checking for pending key bundle for recently-joined room ${roomId} (joined ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()})`);
|
|
await rustCrypto.maybeAcceptKeyBundle(roomId, pendingDetails.inviterId.toString());
|
|
} else {
|
|
logger.info(`Clearing pending-key-bundle flag for room ${roomId} (too old: joined ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()})`);
|
|
await olmMachine.clearRoomPendingKeyBundle(new RustSdkCryptoJs.RoomId(roomId));
|
|
}
|
|
}
|
|
return rustCrypto;
|
|
}
|
|
//# sourceMappingURL=index.js.map
|