From 21a1bdf7b740ec26d864260a42b490823afb54f8 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Tue, 11 Jul 2023 16:18:46 +0200 Subject: [PATCH] Cypress: Add test to verify device after login (#11217) * Run cypress test without `cryptoCallbacks` * Add security phrase test * Add security key test * Fix type import * Move new test in `verification.spec.ts` * The nest tests work with the new crypto * Fix import yupe --- cypress/e2e/crypto/verification.spec.ts | 51 ++++++++++++++++++++++++- cypress/support/bot.ts | 39 ++++++++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) 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; }, );