Skip to content

Commit 614f446

Browse files
authored
Combine QrCodeEvent, SasEvent and VerificationEvent (#3386)
* Move IReciprocateQr to `crypto-api` and rename * Move ISasEvent to `crypto-api`, and rename ... and add some ✨comments✨ * Combine QrCodeEvent, SasEvent and VerificationEvent together ... as a precursor to extracting a single `Verifier` interface for `SAS` and `ReciprocateQRCode`. `enum`s are slightly magical things that have both a type and a value, so we have to re-export their backwards-compatibility fudges twice. * Update src/crypto/verification/Base.ts
1 parent b62fac9 commit 614f446

File tree

5 files changed

+153
-48
lines changed

5 files changed

+153
-48
lines changed

src/crypto-api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,5 @@ export class DeviceVerificationStatus {
260260
return this.localVerified || (this.trustCrossSignedDevices && this.crossSigningVerified);
261261
}
262262
}
263+
264+
export * from "./crypto-api/verification";

src/crypto-api/verification.ts

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
Copyright 2023 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { MatrixEvent } from "../models/event";
18+
19+
/** Events emitted by `Verifier`. */
20+
export enum VerifierEvent {
21+
/**
22+
* The verification has been cancelled, by us or the other side.
23+
*
24+
* The payload is either an {@link Error}, or an (incoming or outgoing) {@link MatrixEvent}, depending on
25+
* unspecified reasons.
26+
*/
27+
Cancel = "cancel",
28+
29+
/**
30+
* SAS data has been exchanged and should be displayed to the user.
31+
*
32+
* The payload is the {@link ShowQrCodeCallbacks} object.
33+
*/
34+
ShowSas = "show_sas",
35+
36+
/**
37+
* QR code data should be displayed to the user.
38+
*
39+
* The payload is the {@link ShowQrCodeCallbacks} object.
40+
*/
41+
ShowReciprocateQr = "show_reciprocate_qr",
42+
}
43+
44+
/** Listener type map for {@link VerifierEvent}s. */
45+
export type VerifierEventHandlerMap = {
46+
[VerifierEvent.Cancel]: (e: Error | MatrixEvent) => void;
47+
[VerifierEvent.ShowSas]: (sas: ShowSasCallbacks) => void;
48+
[VerifierEvent.ShowReciprocateQr]: (qr: ShowQrCodeCallbacks) => void;
49+
};
50+
51+
/**
52+
* Callbacks for user actions while a QR code is displayed.
53+
*
54+
* This is exposed as the payload of a `VerifierEvent.ShowReciprocateQr` event, or can be retrieved directly from the
55+
* verifier as `reciprocateQREvent`.
56+
*/
57+
export interface ShowQrCodeCallbacks {
58+
/** The user confirms that the verification data matches */
59+
confirm(): void;
60+
61+
/** Cancel the verification flow */
62+
cancel(): void;
63+
}
64+
65+
/**
66+
* Callbacks for user actions while a SAS is displayed.
67+
*
68+
* This is exposed as the payload of a `VerifierEvent.ShowSas` event, or directly from the verifier as `sasEvent`.
69+
*/
70+
export interface ShowSasCallbacks {
71+
/** The generated SAS to be shown to the user */
72+
sas: GeneratedSas;
73+
74+
/** Function to call if the user confirms that the SAS matches.
75+
*
76+
* @returns A Promise that completes once the m.key.verification.mac is queued.
77+
*/
78+
confirm(): Promise<void>;
79+
80+
/**
81+
* Function to call if the user finds the SAS does not match.
82+
*
83+
* Sends an `m.key.verification.cancel` event with a `m.mismatched_sas` error code.
84+
*/
85+
mismatch(): void;
86+
87+
/** Cancel the verification flow */
88+
cancel(): void;
89+
}
90+
91+
/** A generated SAS to be shown to the user, in alternative formats */
92+
export interface GeneratedSas {
93+
/**
94+
* The SAS as three numbers between 0 and 8191.
95+
*
96+
* Only populated if the `decimal` SAS method was negotiated.
97+
*/
98+
decimal?: [number, number, number];
99+
100+
/**
101+
* The SAS as seven emojis.
102+
*
103+
* Only populated if the `emoji` SAS method was negotiated.
104+
*/
105+
emoji?: EmojiMapping[];
106+
}
107+
108+
/**
109+
* An emoji for the generated SAS. A tuple `[emoji, name]` where `emoji` is the emoji itself and `name` is the
110+
* English name.
111+
*/
112+
export type EmojiMapping = [emoji: string, name: string];

src/crypto/verification/Base.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import { KeysDuringVerification, requestKeysDuringVerification } from "../CrossS
2828
import { IVerificationChannel } from "./request/Channel";
2929
import { MatrixClient } from "../../client";
3030
import { VerificationRequest } from "./request/VerificationRequest";
31-
import { ListenerMap, TypedEventEmitter } from "../../models/typed-event-emitter";
31+
import { TypedEventEmitter } from "../../models/typed-event-emitter";
32+
import { VerifierEvent, VerifierEventHandlerMap } from "../../crypto-api/verification";
3233

3334
const timeoutException = new Error("Verification timed out");
3435

@@ -40,18 +41,24 @@ export class SwitchStartEventError extends Error {
4041

4142
export type KeyVerifier = (keyId: string, device: DeviceInfo, keyInfo: string) => void;
4243

43-
export enum VerificationEvent {
44-
Cancel = "cancel",
45-
}
44+
/** @deprecated use VerifierEvent */
45+
export type VerificationEvent = VerifierEvent;
46+
/** @deprecated use VerifierEvent */
47+
export const VerificationEvent = VerifierEvent;
4648

49+
/** @deprecated use VerifierEventHandlerMap */
4750
export type VerificationEventHandlerMap = {
4851
[VerificationEvent.Cancel]: (e: Error | MatrixEvent) => void;
4952
};
5053

54+
// The type parameters of VerificationBase are no longer used, but we need some placeholders to maintain
55+
// backwards compatibility with applications that reference the class.
5156
export class VerificationBase<
52-
Events extends string,
53-
Arguments extends ListenerMap<Events | VerificationEvent>,
54-
> extends TypedEventEmitter<Events | VerificationEvent, Arguments, VerificationEventHandlerMap> {
57+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
58+
Events extends string = VerifierEvent,
59+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
60+
Arguments = VerifierEventHandlerMap,
61+
> extends TypedEventEmitter<VerifierEvent, VerifierEventHandlerMap> {
5562
private cancelled = false;
5663
private _done = false;
5764
private promise: Promise<void> | null = null;

src/crypto/verification/QRCode.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,26 @@ limitations under the License.
1818
* QR code key verification.
1919
*/
2020

21-
import { VerificationBase as Base, VerificationEventHandlerMap } from "./Base";
21+
import { VerificationBase as Base } from "./Base";
2222
import { newKeyMismatchError, newUserCancelledError } from "./Error";
2323
import { decodeBase64, encodeUnpaddedBase64 } from "../olmlib";
2424
import { logger } from "../../logger";
2525
import { VerificationRequest } from "./request/VerificationRequest";
2626
import { MatrixClient } from "../../client";
2727
import { IVerificationChannel } from "./request/Channel";
2828
import { MatrixEvent } from "../../models/event";
29+
import { ShowQrCodeCallbacks, VerifierEvent } from "../../crypto-api/verification";
2930

3031
export const SHOW_QR_CODE_METHOD = "m.qr_code.show.v1";
3132
export const SCAN_QR_CODE_METHOD = "m.qr_code.scan.v1";
3233

33-
interface IReciprocateQr {
34-
confirm(): void;
35-
cancel(): void;
36-
}
37-
38-
export enum QrCodeEvent {
39-
ShowReciprocateQr = "show_reciprocate_qr",
40-
}
41-
42-
type EventHandlerMap = {
43-
[QrCodeEvent.ShowReciprocateQr]: (qr: IReciprocateQr) => void;
44-
} & VerificationEventHandlerMap;
34+
/** @deprecated use VerifierEvent */
35+
export type QrCodeEvent = VerifierEvent;
36+
/** @deprecated use VerifierEvent */
37+
export const QrCodeEvent = VerifierEvent;
4538

46-
export class ReciprocateQRCode extends Base<QrCodeEvent, EventHandlerMap> {
47-
public reciprocateQREvent?: IReciprocateQr;
39+
export class ReciprocateQRCode extends Base {
40+
public reciprocateQREvent?: ShowQrCodeCallbacks;
4841

4942
public static factory(
5043
channel: IVerificationChannel,

src/crypto/verification/SAS.ts

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ limitations under the License.
2121
import anotherjson from "another-json";
2222
import { Utility, SAS as OlmSAS } from "@matrix-org/olm";
2323

24-
import { VerificationBase as Base, SwitchStartEventError, VerificationEventHandlerMap } from "./Base";
24+
import { VerificationBase as Base, SwitchStartEventError } from "./Base";
2525
import {
2626
errorFactory,
2727
newInvalidMessageError,
@@ -33,6 +33,14 @@ import { logger } from "../../logger";
3333
import { IContent, MatrixEvent } from "../../models/event";
3434
import { generateDecimalSas } from "./SASDecimal";
3535
import { EventType } from "../../@types/event";
36+
import { EmojiMapping, GeneratedSas, ShowSasCallbacks, VerifierEvent } from "../../crypto-api/verification";
37+
38+
// backwards-compatibility exports
39+
export {
40+
ShowSasCallbacks as ISasEvent,
41+
GeneratedSas as IGeneratedSas,
42+
EmojiMapping,
43+
} from "../../crypto-api/verification";
3644

3745
const START_TYPE = EventType.KeyVerificationStart;
3846

@@ -44,8 +52,6 @@ const newMismatchedSASError = errorFactory("m.mismatched_sas", "Mismatched short
4452

4553
const newMismatchedCommitmentError = errorFactory("m.mismatched_commitment", "Mismatched commitment");
4654

47-
type EmojiMapping = [emoji: string, name: string];
48-
4955
const emojiMapping: EmojiMapping[] = [
5056
["🐶", "dog"], // 0
5157
["🐱", "cat"], // 1
@@ -133,20 +139,8 @@ const sasGenerators = {
133139
emoji: generateEmojiSas,
134140
} as const;
135141

136-
export interface IGeneratedSas {
137-
decimal?: [number, number, number];
138-
emoji?: EmojiMapping[];
139-
}
140-
141-
export interface ISasEvent {
142-
sas: IGeneratedSas;
143-
confirm(): Promise<void>;
144-
cancel(): void;
145-
mismatch(): void;
146-
}
147-
148-
function generateSas(sasBytes: Uint8Array, methods: string[]): IGeneratedSas {
149-
const sas: IGeneratedSas = {};
142+
function generateSas(sasBytes: Uint8Array, methods: string[]): GeneratedSas {
143+
const sas: GeneratedSas = {};
150144
for (const method of methods) {
151145
if (method in sasGenerators) {
152146
// @ts-ignore - ts doesn't like us mixing types like this
@@ -220,19 +214,16 @@ function intersection<T>(anArray: T[], aSet: Set<T>): T[] {
220214
return Array.isArray(anArray) ? anArray.filter((x) => aSet.has(x)) : [];
221215
}
222216

223-
export enum SasEvent {
224-
ShowSas = "show_sas",
225-
}
226-
227-
type EventHandlerMap = {
228-
[SasEvent.ShowSas]: (sas: ISasEvent) => void;
229-
} & VerificationEventHandlerMap;
217+
/** @deprecated use VerifierEvent */
218+
export type SasEvent = VerifierEvent;
219+
/** @deprecated use VerifierEvent */
220+
export const SasEvent = VerifierEvent;
230221

231-
export class SAS extends Base<SasEvent, EventHandlerMap> {
222+
export class SAS extends Base {
232223
private waitingForAccept?: boolean;
233224
public ourSASPubKey?: string;
234225
public theirSASPubKey?: string;
235-
public sasEvent?: ISasEvent;
226+
public sasEvent?: ShowSasCallbacks;
236227

237228
// eslint-disable-next-line @typescript-eslint/naming-convention
238229
public static get NAME(): string {

0 commit comments

Comments
 (0)