diff --git a/cypress/e2e/crypto/verification.spec.ts b/cypress/e2e/crypto/verification.spec.ts index b6ec5f0fbb..d9b4bb6e53 100644 --- a/cypress/e2e/crypto/verification.spec.ts +++ b/cypress/e2e/crypto/verification.spec.ts @@ -36,7 +36,11 @@ describe("Device verification", () => { cy.window({ log: false }).should("have.property", "matrixcs"); // Create a new device for alice - cy.getBot(homeserver, { rustCrypto: true, bootstrapCrossSigning: true }).then((bot) => { + cy.getBot(homeserver, { + rustCrypto: true, + bootstrapCrossSigning: true, + bootstrapSecretStorage: true, + }).then((bot) => { aliceBotClient = bot; }); }); @@ -87,6 +91,51 @@ describe("Device verification", () => { checkDeviceIsCrossSigned(); }); + it("Verify device during login with Security Phrase", () => { + logIntoElement(homeserver.baseUrl, aliceBotClient.getUserId(), aliceBotClient.__cypress_password); + + // Select the security phrase + cy.get(".mx_AuthPage").within(() => { + cy.findByRole("button", { name: "Verify with Security Key or Phrase" }).click(); + }); + + // Fill the passphrase + cy.get(".mx_Dialog").within(() => { + cy.get("input").type("new passphrase"); + cy.contains(".mx_Dialog_primary:not([disabled])", "Continue").click(); + }); + + cy.get(".mx_AuthPage").within(() => { + cy.findByRole("button", { name: "Done" }).click(); + }); + + // Check that our device is now cross-signed + checkDeviceIsCrossSigned(); + }); + + it("Verify device during login with Security Key", () => { + logIntoElement(homeserver.baseUrl, aliceBotClient.getUserId(), aliceBotClient.__cypress_password); + + // Select the security phrase + cy.get(".mx_AuthPage").within(() => { + cy.findByRole("button", { name: "Verify with Security Key or Phrase" }).click(); + }); + + // Fill the security key + cy.get(".mx_Dialog").within(() => { + cy.findByRole("button", { name: "use your Security Key" }).click(); + cy.get("#mx_securityKey").type(aliceBotClient.__cypress_recovery_key.encodedPrivateKey); + cy.contains(".mx_Dialog_primary:not([disabled])", "Continue").click(); + }); + + cy.get(".mx_AuthPage").within(() => { + cy.findByRole("button", { name: "Done" }).click(); + }); + + // Check that our device is now cross-signed + checkDeviceIsCrossSigned(); + }); + it("Handle incoming verification request with SAS", () => { logIntoElement(homeserver.baseUrl, aliceBotClient.getUserId(), aliceBotClient.__cypress_password); diff --git a/cypress/support/bot.ts b/cypress/support/bot.ts index 5806bab7ef..34e5c858a9 100644 --- a/cypress/support/bot.ts +++ b/cypress/support/bot.ts @@ -17,6 +17,8 @@ limitations under the License. /// import type { ISendEventResponse, MatrixClient, Room } from "matrix-js-sdk/src/matrix"; +import type { GeneratedSecretStorageKey } from "matrix-js-sdk/src/crypto-api"; +import type { AddSecretStorageKeyOpts } from "matrix-js-sdk/src/secret-storage"; import { HomeserverInstance } from "../plugins/utils/homeserver"; import { Credentials } from "./homeserver"; import Chainable = Cypress.Chainable; @@ -47,6 +49,10 @@ interface CreateBotOpts { * Whether to use the rust crypto impl. Defaults to false (for now!) */ rustCrypto?: boolean; + /** + * Whether or not to bootstrap the secret storage + */ + bootstrapSecretStorage?: boolean; } const defaultCreateBotOptions = { @@ -58,6 +64,7 @@ const defaultCreateBotOptions = { export interface CypressBot extends MatrixClient { __cypress_password: string; + __cypress_recovery_key: GeneratedSecretStorageKey; } declare global { @@ -143,6 +150,24 @@ function setupBotClient( Object.assign(keys, k); }; + // Store the cached secret storage key and return it when `getSecretStorageKey` is called + let cachedKey: { keyId: string; key: Uint8Array }; + const cacheSecretStorageKey = (keyId: string, keyInfo: AddSecretStorageKeyOpts, key: Uint8Array) => { + cachedKey = { + keyId, + key, + }; + }; + + const getSecretStorageKey = () => Promise.resolve<[string, Uint8Array]>([cachedKey.keyId, cachedKey.key]); + + const cryptoCallbacks = { + getCrossSigningKey, + saveCrossSigningKeys, + cacheSecretStorageKey, + getSecretStorageKey, + }; + const cli = new win.matrixcs.MatrixClient({ baseUrl: homeserver.baseUrl, userId: credentials.userId, @@ -151,7 +176,7 @@ function setupBotClient( store: new win.matrixcs.MemoryStore(), scheduler: new win.matrixcs.MatrixScheduler(), cryptoStore: new win.matrixcs.MemoryCryptoStore(), - cryptoCallbacks: { getCrossSigningKey, saveCrossSigningKeys }, + cryptoCallbacks, }); if (opts.autoAcceptInvites) { @@ -192,6 +217,18 @@ function setupBotClient( }, }); } + + if (opts.bootstrapSecretStorage) { + const passphrase = "new passphrase"; + const recoveryKey = await cli.getCrypto().createRecoveryKeyFromPassphrase(passphrase); + Object.assign(cli, { __cypress_recovery_key: recoveryKey }); + + await cli.getCrypto()!.bootstrapSecretStorage({ + setupNewSecretStorage: true, + createSecretStorageKey: () => Promise.resolve(recoveryKey), + }); + } + return cli; }, );