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: Stub CheckOwnCrossSigningTrust
, import cross signing keys and verify local device in bootstrapCrossSigning
(#3608)
This commit is contained in:
@ -18,9 +18,22 @@ import fetchMock from "fetch-mock-jest";
|
|||||||
import "fake-indexeddb/auto";
|
import "fake-indexeddb/auto";
|
||||||
import { IDBFactory } from "fake-indexeddb";
|
import { IDBFactory } from "fake-indexeddb";
|
||||||
|
|
||||||
import { CRYPTO_BACKENDS, InitCrypto } from "../../test-utils/test-utils";
|
import { CRYPTO_BACKENDS, InitCrypto, syncPromise } from "../../test-utils/test-utils";
|
||||||
import { createClient, IAuthDict, MatrixClient } from "../../../src";
|
import { AuthDict, createClient, CryptoEvent, MatrixClient } from "../../../src";
|
||||||
import { mockSetupCrossSigningRequests } from "../../test-utils/mockEndpoints";
|
import { mockInitialApiRequests, mockSetupCrossSigningRequests } from "../../test-utils/mockEndpoints";
|
||||||
|
import { encryptAES } from "../../../src/crypto/aes";
|
||||||
|
import { CryptoCallbacks } from "../../../src/crypto-api";
|
||||||
|
import { SECRET_STORAGE_ALGORITHM_V1_AES } from "../../../src/secret-storage";
|
||||||
|
import { ISyncResponder, SyncResponder } from "../../test-utils/SyncResponder";
|
||||||
|
import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver";
|
||||||
|
import {
|
||||||
|
MASTER_CROSS_SIGNING_PRIVATE_KEY_BASE64,
|
||||||
|
SELF_CROSS_SIGNING_PRIVATE_KEY_BASE64,
|
||||||
|
SELF_CROSS_SIGNING_PUBLIC_KEY_BASE64,
|
||||||
|
SIGNED_CROSS_SIGNING_KEYS_DATA,
|
||||||
|
USER_CROSS_SIGNING_PRIVATE_KEY_BASE64,
|
||||||
|
} from "../../test-utils/test-data";
|
||||||
|
import { E2EKeyResponder } from "../../test-utils/E2EKeyResponder";
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
// reset fake-indexeddb after each test, to make sure we don't leak connections
|
// reset fake-indexeddb after each test, to make sure we don't leak connections
|
||||||
@ -39,8 +52,32 @@ const TEST_DEVICE_ID = "xzcvb";
|
|||||||
* to provide the most effective integration tests possible.
|
* to provide the most effective integration tests possible.
|
||||||
*/
|
*/
|
||||||
describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: string, initCrypto: InitCrypto) => {
|
describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: string, initCrypto: InitCrypto) => {
|
||||||
|
// newBackendOnly is the opposite to `oldBackendOnly`: it will skip the test if we are running against the legacy
|
||||||
|
// backend. Once we drop support for legacy crypto, it will go away.
|
||||||
|
const newBackendOnly = backend === "rust-sdk" ? test : test.skip;
|
||||||
|
|
||||||
let aliceClient: MatrixClient;
|
let aliceClient: MatrixClient;
|
||||||
|
|
||||||
|
/** an object which intercepts `/sync` requests from {@link #aliceClient} */
|
||||||
|
let syncResponder: ISyncResponder;
|
||||||
|
|
||||||
|
/** an object which intercepts `/keys/query` requests on the test homeserver */
|
||||||
|
let e2eKeyResponder: E2EKeyResponder;
|
||||||
|
|
||||||
|
// Encryption key used to encrypt cross signing keys
|
||||||
|
const encryptionKey = new Uint8Array(32);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the {@link CryptoCallbacks}
|
||||||
|
*/
|
||||||
|
function createCryptoCallbacks(): CryptoCallbacks {
|
||||||
|
return {
|
||||||
|
getSecretStorageKey: (keys, name) => {
|
||||||
|
return Promise.resolve<[string, Uint8Array]>(["key_id", encryptionKey]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
// anything that we don't have a specific matcher for silently returns a 404
|
// anything that we don't have a specific matcher for silently returns a 404
|
||||||
fetchMock.catch(404);
|
fetchMock.catch(404);
|
||||||
@ -52,8 +89,14 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s
|
|||||||
userId: TEST_USER_ID,
|
userId: TEST_USER_ID,
|
||||||
accessToken: "akjgkrgjs",
|
accessToken: "akjgkrgjs",
|
||||||
deviceId: TEST_DEVICE_ID,
|
deviceId: TEST_DEVICE_ID,
|
||||||
|
cryptoCallbacks: createCryptoCallbacks(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
syncResponder = new SyncResponder(homeserverUrl);
|
||||||
|
e2eKeyResponder = new E2EKeyResponder(homeserverUrl);
|
||||||
|
/** an object which intercepts `/keys/upload` requests on the test homeserver */
|
||||||
|
new E2EKeyReceiver(homeserverUrl);
|
||||||
|
|
||||||
await initCrypto(aliceClient);
|
await initCrypto(aliceClient);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -68,7 +111,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s
|
|||||||
* @param authDict - The parameters to as the `auth` dict in the key upload request.
|
* @param authDict - The parameters to as the `auth` dict in the key upload request.
|
||||||
* @see https://spec.matrix.org/v1.6/client-server-api/#authentication-types
|
* @see https://spec.matrix.org/v1.6/client-server-api/#authentication-types
|
||||||
*/
|
*/
|
||||||
async function bootstrapCrossSigning(authDict: IAuthDict): Promise<void> {
|
async function bootstrapCrossSigning(authDict: AuthDict): Promise<void> {
|
||||||
await aliceClient.getCrypto()?.bootstrapCrossSigning({
|
await aliceClient.getCrypto()?.bootstrapCrossSigning({
|
||||||
authUploadDeviceSigningKeys: (makeRequest) => makeRequest(authDict).then(() => undefined),
|
authUploadDeviceSigningKeys: (makeRequest) => makeRequest(authDict).then(() => undefined),
|
||||||
});
|
});
|
||||||
@ -105,6 +148,94 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("cross-signing (%s)", (backend: s
|
|||||||
`[${TEST_USER_ID}].[${TEST_DEVICE_ID}].signatures.[${TEST_USER_ID}].[${sskId}]`,
|
`[${TEST_USER_ID}].[${TEST_DEVICE_ID}].signatures.[${TEST_USER_ID}].[${sskId}]`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
newBackendOnly("get cross signing keys from secret storage and import them", async () => {
|
||||||
|
// Return public cross signing keys
|
||||||
|
e2eKeyResponder.addCrossSigningData(SIGNED_CROSS_SIGNING_KEYS_DATA);
|
||||||
|
|
||||||
|
mockInitialApiRequests(aliceClient.getHomeserverUrl());
|
||||||
|
|
||||||
|
// Encrypt the private keys and return them in the /sync response as if they are in Secret Storage
|
||||||
|
const masterKey = await encryptAES(
|
||||||
|
MASTER_CROSS_SIGNING_PRIVATE_KEY_BASE64,
|
||||||
|
encryptionKey,
|
||||||
|
"m.cross_signing.master",
|
||||||
|
);
|
||||||
|
const selfSigningKey = await encryptAES(
|
||||||
|
SELF_CROSS_SIGNING_PRIVATE_KEY_BASE64,
|
||||||
|
encryptionKey,
|
||||||
|
"m.cross_signing.self_signing",
|
||||||
|
);
|
||||||
|
const userSigningKey = await encryptAES(
|
||||||
|
USER_CROSS_SIGNING_PRIVATE_KEY_BASE64,
|
||||||
|
encryptionKey,
|
||||||
|
"m.cross_signing.user_signing",
|
||||||
|
);
|
||||||
|
|
||||||
|
syncResponder.sendOrQueueSyncResponse({
|
||||||
|
next_batch: 1,
|
||||||
|
account_data: {
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
type: "m.cross_signing.master",
|
||||||
|
content: {
|
||||||
|
encrypted: {
|
||||||
|
key_id: masterKey,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "m.cross_signing.self_signing",
|
||||||
|
content: {
|
||||||
|
encrypted: {
|
||||||
|
key_id: selfSigningKey,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "m.cross_signing.user_signing",
|
||||||
|
content: {
|
||||||
|
encrypted: {
|
||||||
|
key_id: userSigningKey,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "m.secret_storage.key.key_id",
|
||||||
|
content: {
|
||||||
|
key: "key_id",
|
||||||
|
algorithm: SECRET_STORAGE_ALGORITHM_V1_AES,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await aliceClient.startClient();
|
||||||
|
await syncPromise(aliceClient);
|
||||||
|
|
||||||
|
// we expect a request to upload signatures for our device ...
|
||||||
|
fetchMock.post({ url: "path:/_matrix/client/v3/keys/signatures/upload", name: "upload-sigs" }, {});
|
||||||
|
|
||||||
|
// we expect the UserTrustStatusChanged event to be fired after the cross signing keys import
|
||||||
|
const userTrustStatusChangedPromise = new Promise<string>((resolve) =>
|
||||||
|
aliceClient.on(CryptoEvent.UserTrustStatusChanged, resolve),
|
||||||
|
);
|
||||||
|
|
||||||
|
const authDict = { type: "test" };
|
||||||
|
await bootstrapCrossSigning(authDict);
|
||||||
|
|
||||||
|
// Check if the UserTrustStatusChanged event was fired
|
||||||
|
expect(await userTrustStatusChangedPromise).toBe(aliceClient.getUserId());
|
||||||
|
|
||||||
|
// Expect the signature to be uploaded
|
||||||
|
expect(fetchMock.called("upload-sigs")).toBeTruthy();
|
||||||
|
const [, sigsOpts] = fetchMock.lastCall("upload-sigs")!;
|
||||||
|
const body = JSON.parse(sigsOpts!.body as string);
|
||||||
|
// the device should have a signature with the public self cross signing keys.
|
||||||
|
expect(body).toHaveProperty(
|
||||||
|
`[${TEST_USER_ID}].[${TEST_DEVICE_ID}].signatures.[${TEST_USER_ID}].[ed25519:${SELF_CROSS_SIGNING_PUBLIC_KEY_BASE64}]`,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getCrossSigningStatus()", () => {
|
describe("getCrossSigningStatus()", () => {
|
||||||
|
@ -32,13 +32,16 @@ export function mockInitialApiRequests(homeserverUrl: string) {
|
|||||||
/**
|
/**
|
||||||
* Mock the requests needed to set up cross signing
|
* Mock the requests needed to set up cross signing
|
||||||
*
|
*
|
||||||
* Return `{}` for `GET _matrix/client/r0/user/:userId/account_data/:type` request
|
* Return 404 error for `GET _matrix/client/r0/user/:userId/account_data/:type` request
|
||||||
* Return `{}` for `POST _matrix/client/v3/keys/signatures/upload` request (named `upload-sigs` for fetchMock check)
|
* Return `{}` for `POST _matrix/client/v3/keys/signatures/upload` request (named `upload-sigs` for fetchMock check)
|
||||||
* Return `{}` for `POST /_matrix/client/(unstable|v3)/keys/device_signing/upload` request (named `upload-keys` for fetchMock check)
|
* Return `{}` for `POST /_matrix/client/(unstable|v3)/keys/device_signing/upload` request (named `upload-keys` for fetchMock check)
|
||||||
*/
|
*/
|
||||||
export function mockSetupCrossSigningRequests(): void {
|
export function mockSetupCrossSigningRequests(): void {
|
||||||
// have account_data requests return an empty object
|
// have account_data requests return an empty object
|
||||||
fetchMock.get("express:/_matrix/client/r0/user/:userId/account_data/:type", {});
|
fetchMock.get("express:/_matrix/client/r0/user/:userId/account_data/:type", {
|
||||||
|
status: 404,
|
||||||
|
body: { errcode: "M_NOT_FOUND", error: "Account data not found." },
|
||||||
|
});
|
||||||
|
|
||||||
// we expect a request to upload signatures for our device ...
|
// we expect a request to upload signatures for our device ...
|
||||||
fetchMock.post({ url: "path:/_matrix/client/v3/keys/signatures/upload", name: "upload-sigs" }, {});
|
fetchMock.post({ url: "path:/_matrix/client/v3/keys/signatures/upload", name: "upload-sigs" }, {});
|
||||||
|
@ -71,6 +71,29 @@ def main() -> None:
|
|||||||
b64_master_public_key = encode_base64(
|
b64_master_public_key = encode_base64(
|
||||||
master_private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
|
master_private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
|
||||||
)
|
)
|
||||||
|
b64_master_private_key = encode_base64(
|
||||||
|
MASTER_CROSS_SIGNING_PRIVATE_KEY_BYTES
|
||||||
|
)
|
||||||
|
|
||||||
|
self_signing_private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
|
||||||
|
SELF_CROSS_SIGNING_PRIVATE_KEY_BYTES
|
||||||
|
)
|
||||||
|
b64_self_signing_public_key = encode_base64(
|
||||||
|
self_signing_private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
|
||||||
|
)
|
||||||
|
b64_self_signing_private_key = encode_base64(
|
||||||
|
SELF_CROSS_SIGNING_PRIVATE_KEY_BYTES
|
||||||
|
)
|
||||||
|
|
||||||
|
user_signing_private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
|
||||||
|
USER_CROSS_SIGNING_PRIVATE_KEY_BYTES
|
||||||
|
)
|
||||||
|
b64_user_signing_public_key = encode_base64(
|
||||||
|
user_signing_private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
|
||||||
|
)
|
||||||
|
b64_user_signing_private_key = encode_base64(
|
||||||
|
USER_CROSS_SIGNING_PRIVATE_KEY_BYTES
|
||||||
|
)
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f"""\
|
f"""\
|
||||||
@ -96,6 +119,21 @@ export const SIGNED_TEST_DEVICE_DATA: IDeviceKeys = {json.dumps(device_data, ind
|
|||||||
/** base64-encoded public master cross-signing key */
|
/** base64-encoded public master cross-signing key */
|
||||||
export const MASTER_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "{b64_master_public_key}";
|
export const MASTER_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "{b64_master_public_key}";
|
||||||
|
|
||||||
|
/** base64-encoded private master cross-signing key */
|
||||||
|
export const MASTER_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "{b64_master_private_key}";
|
||||||
|
|
||||||
|
/** base64-encoded public self cross-signing key */
|
||||||
|
export const SELF_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "{b64_self_signing_public_key}";
|
||||||
|
|
||||||
|
/** base64-encoded private self signing cross-signing key */
|
||||||
|
export const SELF_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "{b64_self_signing_private_key}";
|
||||||
|
|
||||||
|
/** base64-encoded public user cross-signing key */
|
||||||
|
export const USER_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "{b64_user_signing_public_key}";
|
||||||
|
|
||||||
|
/** base64-encoded private user signing cross-signing key */
|
||||||
|
export const USER_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "{b64_user_signing_private_key}";
|
||||||
|
|
||||||
/** Signed cross-signing keys data, also suitable for returning from a `/keys/query` call */
|
/** Signed cross-signing keys data, also suitable for returning from a `/keys/query` call */
|
||||||
export const SIGNED_CROSS_SIGNING_KEYS_DATA: Partial<IDownloadKeyResult> = {
|
export const SIGNED_CROSS_SIGNING_KEYS_DATA: Partial<IDownloadKeyResult> = {
|
||||||
json.dumps(build_cross_signing_keys_data(), indent=4)
|
json.dumps(build_cross_signing_keys_data(), indent=4)
|
||||||
|
@ -36,6 +36,21 @@ export const SIGNED_TEST_DEVICE_DATA: IDeviceKeys = {
|
|||||||
/** base64-encoded public master cross-signing key */
|
/** base64-encoded public master cross-signing key */
|
||||||
export const MASTER_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "J+5An10v1vzZpAXTYFokD1/PEVccFnLC61EfRXit0UY";
|
export const MASTER_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "J+5An10v1vzZpAXTYFokD1/PEVccFnLC61EfRXit0UY";
|
||||||
|
|
||||||
|
/** base64-encoded private master cross-signing key */
|
||||||
|
export const MASTER_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "ZG95b3VzcGVha3doYWFhYWFhYWFhYWFhYWFhYWFhbGU";
|
||||||
|
|
||||||
|
/** base64-encoded public self cross-signing key */
|
||||||
|
export const SELF_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "aU2+2CyXQTCuDcmWW0EL2bhJ6PdjFW2LbAsbHqf02AY";
|
||||||
|
|
||||||
|
/** base64-encoded private self signing cross-signing key */
|
||||||
|
export const SELF_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "c2VsZnNlbGZzZWxmc2VsZnNlbGZzZWxmc2VsZnNlbGY";
|
||||||
|
|
||||||
|
/** base64-encoded public user cross-signing key */
|
||||||
|
export const USER_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "g5TC/zjQXyZYuDLZv7a41z5fFVrXpYPypG//AFQj8hY";
|
||||||
|
|
||||||
|
/** base64-encoded private user signing cross-signing key */
|
||||||
|
export const USER_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "dXNlcnVzZXJ1c2VydXNlcnVzZXJ1c2VydXNlcnVzZXI";
|
||||||
|
|
||||||
/** Signed cross-signing keys data, also suitable for returning from a `/keys/query` call */
|
/** Signed cross-signing keys data, also suitable for returning from a `/keys/query` call */
|
||||||
export const SIGNED_CROSS_SIGNING_KEYS_DATA: Partial<IDownloadKeyResult> = {
|
export const SIGNED_CROSS_SIGNING_KEYS_DATA: Partial<IDownloadKeyResult> = {
|
||||||
"master_keys": {
|
"master_keys": {
|
||||||
|
@ -19,6 +19,7 @@ import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
|||||||
|
|
||||||
import { CrossSigningIdentity } from "../../../src/rust-crypto/CrossSigningIdentity";
|
import { CrossSigningIdentity } from "../../../src/rust-crypto/CrossSigningIdentity";
|
||||||
import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor";
|
import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor";
|
||||||
|
import { ServerSideSecretStorage } from "../../../src/secret-storage";
|
||||||
|
|
||||||
describe("CrossSigningIdentity", () => {
|
describe("CrossSigningIdentity", () => {
|
||||||
describe("bootstrapCrossSigning", () => {
|
describe("bootstrapCrossSigning", () => {
|
||||||
@ -31,6 +32,9 @@ describe("CrossSigningIdentity", () => {
|
|||||||
/** A mock OutgoingRequestProcessor which crossSigning is connected to */
|
/** A mock OutgoingRequestProcessor which crossSigning is connected to */
|
||||||
let outgoingRequestProcessor: Mocked<OutgoingRequestProcessor>;
|
let outgoingRequestProcessor: Mocked<OutgoingRequestProcessor>;
|
||||||
|
|
||||||
|
/** A mock ServerSideSecretStorage which crossSigning is connected to */
|
||||||
|
let secretStorage: Mocked<ServerSideSecretStorage>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await RustSdkCryptoJs.initAsync();
|
await RustSdkCryptoJs.initAsync();
|
||||||
|
|
||||||
@ -44,7 +48,11 @@ describe("CrossSigningIdentity", () => {
|
|||||||
makeOutgoingRequest: jest.fn(),
|
makeOutgoingRequest: jest.fn(),
|
||||||
} as unknown as Mocked<OutgoingRequestProcessor>;
|
} as unknown as Mocked<OutgoingRequestProcessor>;
|
||||||
|
|
||||||
crossSigning = new CrossSigningIdentity(olmMachine, outgoingRequestProcessor);
|
secretStorage = {
|
||||||
|
get: jest.fn(),
|
||||||
|
} as unknown as Mocked<ServerSideSecretStorage>;
|
||||||
|
|
||||||
|
crossSigning = new CrossSigningIdentity(olmMachine, outgoingRequestProcessor, secretStorage, jest.fn());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should do nothing if keys are present on-device and in secret storage", async () => {
|
it("should do nothing if keys are present on-device and in secret storage", async () => {
|
||||||
|
@ -2250,7 +2250,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
this.on(RoomMemberEvent.Membership, rustCrypto.onRoomMembership.bind(rustCrypto));
|
this.on(RoomMemberEvent.Membership, rustCrypto.onRoomMembership.bind(rustCrypto));
|
||||||
|
|
||||||
// re-emit the events emitted by the crypto impl
|
// re-emit the events emitted by the crypto impl
|
||||||
this.reEmitter.reEmit(rustCrypto, [CryptoEvent.VerificationRequestReceived]);
|
this.reEmitter.reEmit(rustCrypto, [
|
||||||
|
CryptoEvent.VerificationRequestReceived,
|
||||||
|
CryptoEvent.UserTrustStatusChanged,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2679,12 +2682,14 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
* Check the copy of our cross-signing key that we have in the device list and
|
* Check the copy of our cross-signing key that we have in the device list and
|
||||||
* see if we can get the private key. If so, mark it as trusted.
|
* see if we can get the private key. If so, mark it as trusted.
|
||||||
* @param opts - ICheckOwnCrossSigningTrustOpts object
|
* @param opts - ICheckOwnCrossSigningTrustOpts object
|
||||||
|
*
|
||||||
|
* @deprecated Unneeded for the new crypto
|
||||||
*/
|
*/
|
||||||
public checkOwnCrossSigningTrust(opts?: ICheckOwnCrossSigningTrustOpts): Promise<void> {
|
public checkOwnCrossSigningTrust(opts?: ICheckOwnCrossSigningTrustOpts): Promise<void> {
|
||||||
if (!this.crypto) {
|
if (!this.cryptoBackend) {
|
||||||
throw new Error("End-to-end encryption disabled");
|
throw new Error("End-to-end encryption disabled");
|
||||||
}
|
}
|
||||||
return this.crypto.checkOwnCrossSigningTrust(opts);
|
return this.cryptoBackend.checkOwnCrossSigningTrust(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,6 +90,15 @@ export interface CryptoBackend extends SyncCryptoCallbacks, CryptoApi {
|
|||||||
* @returns the cross signing information for the user.
|
* @returns the cross signing information for the user.
|
||||||
*/
|
*/
|
||||||
getStoredCrossSigningForUser(userId: string): CrossSigningInfo | null;
|
getStoredCrossSigningForUser(userId: string): CrossSigningInfo | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the cross signing trust of the current user
|
||||||
|
*
|
||||||
|
* @param opts - Options object.
|
||||||
|
*
|
||||||
|
* @deprecated Unneeded for the new crypto
|
||||||
|
*/
|
||||||
|
checkOwnCrossSigningTrust(opts?: CheckOwnCrossSigningTrustOpts): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The methods which crypto implementations should expose to the Sync api
|
/** The methods which crypto implementations should expose to the Sync api
|
||||||
@ -165,3 +174,10 @@ export interface OnSyncCompletedData {
|
|||||||
*/
|
*/
|
||||||
catchingUp?: boolean;
|
catchingUp?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options object for {@link CryptoBackend#checkOwnCrossSigningTrust}.
|
||||||
|
*/
|
||||||
|
export interface CheckOwnCrossSigningTrustOpts {
|
||||||
|
allowPrivateKeyRequests?: boolean;
|
||||||
|
}
|
||||||
|
@ -15,11 +15,13 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { OlmMachine, CrossSigningStatus } from "@matrix-org/matrix-sdk-crypto-wasm";
|
import { OlmMachine, CrossSigningStatus } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||||
|
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||||
|
|
||||||
import { BootstrapCrossSigningOpts } from "../crypto-api";
|
import { BootstrapCrossSigningOpts } from "../crypto-api";
|
||||||
import { logger } from "../logger";
|
import { logger } from "../logger";
|
||||||
import { OutgoingRequest, OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
|
import { OutgoingRequest, OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
|
||||||
import { UIAuthCallback } from "../interactive-auth";
|
import { UIAuthCallback } from "../interactive-auth";
|
||||||
|
import { ServerSideSecretStorage } from "../secret-storage";
|
||||||
|
|
||||||
/** Manages the cross-signing keys for our own user.
|
/** Manages the cross-signing keys for our own user.
|
||||||
*
|
*
|
||||||
@ -29,6 +31,9 @@ export class CrossSigningIdentity {
|
|||||||
public constructor(
|
public constructor(
|
||||||
private readonly olmMachine: OlmMachine,
|
private readonly olmMachine: OlmMachine,
|
||||||
private readonly outgoingRequestProcessor: OutgoingRequestProcessor,
|
private readonly outgoingRequestProcessor: OutgoingRequestProcessor,
|
||||||
|
private readonly secretStorage: ServerSideSecretStorage,
|
||||||
|
/** Called if the cross signing keys are imported from the secret storage */
|
||||||
|
private readonly onCrossSigningKeysImport: () => void,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,7 +46,15 @@ export class CrossSigningIdentity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const olmDeviceStatus: CrossSigningStatus = await this.olmMachine.crossSigningStatus();
|
const olmDeviceStatus: CrossSigningStatus = await this.olmMachine.crossSigningStatus();
|
||||||
const privateKeysInSecretStorage = false; // TODO
|
|
||||||
|
// Try to fetch cross signing keys from the secret storage
|
||||||
|
const masterKeyFromSecretStorage = await this.secretStorage.get("m.cross_signing.master");
|
||||||
|
const selfSigningKeyFromSecretStorage = await this.secretStorage.get("m.cross_signing.self_signing");
|
||||||
|
const userSigningKeyFromSecretStorage = await this.secretStorage.get("m.cross_signing.user_signing");
|
||||||
|
const privateKeysInSecretStorage = Boolean(
|
||||||
|
masterKeyFromSecretStorage && selfSigningKeyFromSecretStorage && userSigningKeyFromSecretStorage,
|
||||||
|
);
|
||||||
|
|
||||||
const olmDeviceHasKeys =
|
const olmDeviceHasKeys =
|
||||||
olmDeviceStatus.hasMaster && olmDeviceStatus.hasUserSigning && olmDeviceStatus.hasSelfSigning;
|
olmDeviceStatus.hasMaster && olmDeviceStatus.hasUserSigning && olmDeviceStatus.hasSelfSigning;
|
||||||
|
|
||||||
@ -67,7 +80,23 @@ export class CrossSigningIdentity {
|
|||||||
"bootStrapCrossSigning: Cross-signing private keys not found locally, but they are available " +
|
"bootStrapCrossSigning: Cross-signing private keys not found locally, but they are available " +
|
||||||
"in secret storage, reading storage and caching locally",
|
"in secret storage, reading storage and caching locally",
|
||||||
);
|
);
|
||||||
throw new Error("TODO");
|
await this.olmMachine.importCrossSigningKeys(
|
||||||
|
masterKeyFromSecretStorage,
|
||||||
|
selfSigningKeyFromSecretStorage,
|
||||||
|
userSigningKeyFromSecretStorage,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the current device
|
||||||
|
const device: RustSdkCryptoJs.Device = await this.olmMachine.getDevice(
|
||||||
|
this.olmMachine.userId,
|
||||||
|
this.olmMachine.deviceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sign the device with our cross-signing key and upload the signature
|
||||||
|
const request: RustSdkCryptoJs.SignatureUploadRequest = await device.verify();
|
||||||
|
await this.outgoingRequestProcessor.makeOutgoingRequest(request);
|
||||||
|
|
||||||
|
this.onCrossSigningKeysImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we might previously have bootstrapped cross-signing but not completed uploading the keys to the
|
// TODO: we might previously have bootstrapped cross-signing but not completed uploading the keys to the
|
||||||
|
@ -108,7 +108,17 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
|||||||
this.outgoingRequestProcessor = new OutgoingRequestProcessor(olmMachine, http);
|
this.outgoingRequestProcessor = new OutgoingRequestProcessor(olmMachine, http);
|
||||||
this.keyClaimManager = new KeyClaimManager(olmMachine, this.outgoingRequestProcessor);
|
this.keyClaimManager = new KeyClaimManager(olmMachine, this.outgoingRequestProcessor);
|
||||||
this.eventDecryptor = new EventDecryptor(olmMachine);
|
this.eventDecryptor = new EventDecryptor(olmMachine);
|
||||||
this.crossSigningIdentity = new CrossSigningIdentity(olmMachine, this.outgoingRequestProcessor);
|
|
||||||
|
// Fire if the cross signing keys are imported from the secret storage
|
||||||
|
const onCrossSigningKeysImport = (): void => {
|
||||||
|
this.emit(CryptoEvent.UserTrustStatusChanged, this.userId, this.checkUserTrust(this.userId));
|
||||||
|
};
|
||||||
|
this.crossSigningIdentity = new CrossSigningIdentity(
|
||||||
|
olmMachine,
|
||||||
|
this.outgoingRequestProcessor,
|
||||||
|
secretStorage,
|
||||||
|
onCrossSigningKeysImport,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -194,6 +204,20 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is unneeded for the rust-crypto.
|
||||||
|
* The cross signing key import and the device verification are done in {@link CryptoApi#bootstrapCrossSigning}
|
||||||
|
*
|
||||||
|
* The function is stub to keep the compatibility with the old crypto.
|
||||||
|
* More information: https://github.com/vector-im/element-web/issues/25648
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Implementation of {@link CryptoBackend#checkOwnCrossSigningTrust}
|
||||||
|
*/
|
||||||
|
public async checkOwnCrossSigningTrust(): Promise<void> {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// CryptoApi implementation
|
// CryptoApi implementation
|
||||||
@ -1019,11 +1043,16 @@ class EventDecryptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RustCryptoEvents = CryptoEvent.VerificationRequestReceived;
|
type RustCryptoEvents = CryptoEvent.VerificationRequestReceived | CryptoEvent.UserTrustStatusChanged;
|
||||||
|
|
||||||
type RustCryptoEventMap = {
|
type RustCryptoEventMap = {
|
||||||
/**
|
/**
|
||||||
* Fires when a key verification request is received.
|
* Fires when a key verification request is received.
|
||||||
*/
|
*/
|
||||||
[CryptoEvent.VerificationRequestReceived]: (request: VerificationRequest) => void;
|
[CryptoEvent.VerificationRequestReceived]: (request: VerificationRequest) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires when the cross signing keys are imported during {@link CryptoApi#bootstrapCrossSigning}
|
||||||
|
*/
|
||||||
|
[CryptoEvent.UserTrustStatusChanged]: (userId: string, userTrustLevel: UserTrustLevel) => void;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user