You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-06 12:02:40 +03:00
Element-R: Implement VerificationRequest.accept
(#3526)
* Pass `supportedVerificationMethods` into `VerificationRequest` ... so that the application can later call `accept()` and we know what to send. * Implement `VerificationRequest.accept` * Implement `VerificationRequest.declining` * Update src/rust-crypto/verification.ts
This commit is contained in:
committed by
GitHub
parent
c271e1533a
commit
3a8a1389f5
@@ -541,24 +541,18 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("Incoming verification from another device", () => {
|
describe("Incoming verification from another device", () => {
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
e2eKeyResponder.addDeviceKeys(TEST_USER_ID, TEST_DEVICE_ID, SIGNED_TEST_DEVICE_DATA);
|
e2eKeyResponder.addDeviceKeys(TEST_USER_ID, TEST_DEVICE_ID, SIGNED_TEST_DEVICE_DATA);
|
||||||
|
|
||||||
|
aliceClient = await startTestClient();
|
||||||
|
await waitForDeviceList();
|
||||||
});
|
});
|
||||||
|
|
||||||
oldBackendOnly("Incoming verification: can accept", async () => {
|
it("Incoming verification: can accept", async () => {
|
||||||
aliceClient = await startTestClient();
|
|
||||||
const TRANSACTION_ID = "abcd";
|
const TRANSACTION_ID = "abcd";
|
||||||
|
|
||||||
// Initiate the request by sending a to-device message
|
// Initiate the request by sending a to-device message
|
||||||
returnToDeviceMessageFromSync({
|
returnToDeviceMessageFromSync(buildRequestMessage(TRANSACTION_ID));
|
||||||
type: "m.key.verification.request",
|
|
||||||
content: {
|
|
||||||
from_device: TEST_DEVICE_ID,
|
|
||||||
methods: ["m.sas.v1"],
|
|
||||||
transaction_id: TRANSACTION_ID,
|
|
||||||
timestamp: Date.now() - 1000,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const request: VerificationRequest = await emitPromise(
|
const request: VerificationRequest = await emitPromise(
|
||||||
aliceClient,
|
aliceClient,
|
||||||
CryptoEvent.VerificationRequestReceived,
|
CryptoEvent.VerificationRequestReceived,
|
||||||
@@ -585,6 +579,31 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st
|
|||||||
expect(toDeviceMessage.from_device).toEqual(aliceClient.deviceId);
|
expect(toDeviceMessage.from_device).toEqual(aliceClient.deviceId);
|
||||||
expect(toDeviceMessage.transaction_id).toEqual(TRANSACTION_ID);
|
expect(toDeviceMessage.transaction_id).toEqual(TRANSACTION_ID);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Incoming verification: can refuse", async () => {
|
||||||
|
const TRANSACTION_ID = "abcd";
|
||||||
|
|
||||||
|
// Initiate the request by sending a to-device message
|
||||||
|
returnToDeviceMessageFromSync(buildRequestMessage(TRANSACTION_ID));
|
||||||
|
const request: VerificationRequest = await emitPromise(
|
||||||
|
aliceClient,
|
||||||
|
CryptoEvent.VerificationRequestReceived,
|
||||||
|
);
|
||||||
|
expect(request.transactionId).toEqual(TRANSACTION_ID);
|
||||||
|
|
||||||
|
// Alice declines, by sending a cancellation
|
||||||
|
const sendToDevicePromise = expectSendToDeviceMessage("m.key.verification.cancel");
|
||||||
|
const cancelPromise = request.cancel();
|
||||||
|
expect(canAcceptVerificationRequest(request)).toBe(false);
|
||||||
|
expect(request.accepting).toBe(false);
|
||||||
|
expect(request.declining).toBe(true);
|
||||||
|
await cancelPromise;
|
||||||
|
const requestBody = await sendToDevicePromise;
|
||||||
|
expect(request.phase).toEqual(VerificationPhase.Cancelled);
|
||||||
|
|
||||||
|
const toDeviceMessage = requestBody.messages[TEST_USER_ID][TEST_DEVICE_ID];
|
||||||
|
expect(toDeviceMessage.transaction_id).toEqual(TRANSACTION_ID);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function startTestClient(opts: Partial<ICreateClientOpts> = {}): Promise<MatrixClient> {
|
async function startTestClient(opts: Partial<ICreateClientOpts> = {}): Promise<MatrixClient> {
|
||||||
@@ -668,6 +687,19 @@ function encodeUnpaddedBase64(uint8Array: ArrayBuffer | Uint8Array): string {
|
|||||||
return Buffer.from(uint8Array).toString("base64").replace(/=+$/g, "");
|
return Buffer.from(uint8Array).toString("base64").replace(/=+$/g, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** build an m.key.verification.request to-device message originating from the dummy device */
|
||||||
|
function buildRequestMessage(transactionId: string): { type: string; content: object } {
|
||||||
|
return {
|
||||||
|
type: "m.key.verification.request",
|
||||||
|
content: {
|
||||||
|
from_device: TEST_DEVICE_ID,
|
||||||
|
methods: ["m.sas.v1"],
|
||||||
|
transaction_id: transactionId,
|
||||||
|
timestamp: Date.now() - 1000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** build an m.key.verification.ready to-device message originating from the dummy device */
|
/** build an m.key.verification.ready to-device message originating from the dummy device */
|
||||||
function buildReadyMessage(transactionId: string, methods: string[]): { type: string; content: object } {
|
function buildReadyMessage(transactionId: string, methods: string[]): { type: string; content: object } {
|
||||||
return {
|
return {
|
||||||
|
@@ -32,7 +32,7 @@ describe("VerificationRequest", () => {
|
|||||||
startSas: jest.fn(),
|
startSas: jest.fn(),
|
||||||
} as unknown as Mocked<RustSdkCryptoJs.VerificationRequest>;
|
} as unknown as Mocked<RustSdkCryptoJs.VerificationRequest>;
|
||||||
mockedOutgoingRequestProcessor = {} as Mocked<OutgoingRequestProcessor>;
|
mockedOutgoingRequestProcessor = {} as Mocked<OutgoingRequestProcessor>;
|
||||||
request = new RustVerificationRequest(mockedInner, mockedOutgoingRequestProcessor);
|
request = new RustVerificationRequest(mockedInner, mockedOutgoingRequestProcessor, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not permit methods other than SAS", async () => {
|
it("does not permit methods other than SAS", async () => {
|
||||||
|
@@ -553,7 +553,14 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
|||||||
);
|
);
|
||||||
return requests
|
return requests
|
||||||
.filter((request) => request.roomId === undefined)
|
.filter((request) => request.roomId === undefined)
|
||||||
.map((request) => new RustVerificationRequest(request, this.outgoingRequestProcessor));
|
.map(
|
||||||
|
(request) =>
|
||||||
|
new RustVerificationRequest(
|
||||||
|
request,
|
||||||
|
this.outgoingRequestProcessor,
|
||||||
|
this.supportedVerificationMethods,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -600,7 +607,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
|||||||
this.supportedVerificationMethods?.map(verificationMethodIdentifierToMethod),
|
this.supportedVerificationMethods?.map(verificationMethodIdentifierToMethod),
|
||||||
);
|
);
|
||||||
await this.outgoingRequestProcessor.makeOutgoingRequest(outgoingRequest);
|
await this.outgoingRequestProcessor.makeOutgoingRequest(outgoingRequest);
|
||||||
return new RustVerificationRequest(request, this.outgoingRequestProcessor);
|
return new RustVerificationRequest(request, this.outgoingRequestProcessor, this.supportedVerificationMethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -630,7 +637,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
|||||||
this.supportedVerificationMethods?.map(verificationMethodIdentifierToMethod),
|
this.supportedVerificationMethods?.map(verificationMethodIdentifierToMethod),
|
||||||
);
|
);
|
||||||
await this.outgoingRequestProcessor.makeOutgoingRequest(outgoingRequest);
|
await this.outgoingRequestProcessor.makeOutgoingRequest(outgoingRequest);
|
||||||
return new RustVerificationRequest(request, this.outgoingRequestProcessor);
|
return new RustVerificationRequest(request, this.outgoingRequestProcessor, this.supportedVerificationMethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -784,7 +791,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
|||||||
if (request) {
|
if (request) {
|
||||||
this.emit(
|
this.emit(
|
||||||
CryptoEvent.VerificationRequestReceived,
|
CryptoEvent.VerificationRequestReceived,
|
||||||
new RustVerificationRequest(request, this.outgoingRequestProcessor),
|
new RustVerificationRequest(request, this.outgoingRequestProcessor, this.supportedVerificationMethods),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,11 +38,25 @@ export class RustVerificationRequest
|
|||||||
extends TypedEventEmitter<VerificationRequestEvent, VerificationRequestEventHandlerMap>
|
extends TypedEventEmitter<VerificationRequestEvent, VerificationRequestEventHandlerMap>
|
||||||
implements VerificationRequest
|
implements VerificationRequest
|
||||||
{
|
{
|
||||||
|
/** Are we in the process of sending an `m.key.verification.ready` event? */
|
||||||
|
private _accepting = false;
|
||||||
|
|
||||||
|
/** Are we in the process of sending an `m.key.verification.cancellation` event? */
|
||||||
|
private _cancelling = false;
|
||||||
|
|
||||||
private _verifier: Verifier | undefined;
|
private _verifier: Verifier | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new RustVerificationRequest to wrap the rust-level `VerificationRequest`.
|
||||||
|
*
|
||||||
|
* @param inner - VerificationRequest from the Rust SDK
|
||||||
|
* @param outgoingRequestProcessor - `OutgoingRequestProcessor` to use for making outgoing HTTP requests
|
||||||
|
* @param supportedVerificationMethods - Verification methods to use when `accept()` is called
|
||||||
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly inner: RustSdkCryptoJs.VerificationRequest,
|
private readonly inner: RustSdkCryptoJs.VerificationRequest,
|
||||||
private readonly outgoingRequestProcessor: OutgoingRequestProcessor,
|
private readonly outgoingRequestProcessor: OutgoingRequestProcessor,
|
||||||
|
private readonly supportedVerificationMethods: string[] | undefined,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -113,7 +127,9 @@ export class RustVerificationRequest
|
|||||||
case RustSdkCryptoJs.VerificationRequestPhase.Requested:
|
case RustSdkCryptoJs.VerificationRequestPhase.Requested:
|
||||||
return VerificationPhase.Requested;
|
return VerificationPhase.Requested;
|
||||||
case RustSdkCryptoJs.VerificationRequestPhase.Ready:
|
case RustSdkCryptoJs.VerificationRequestPhase.Ready:
|
||||||
return VerificationPhase.Ready;
|
// if we're still sending the `m.key.verification.ready`, that counts as "Requested" in the js-sdk's
|
||||||
|
// parlance.
|
||||||
|
return this._accepting ? VerificationPhase.Requested : VerificationPhase.Ready;
|
||||||
case RustSdkCryptoJs.VerificationRequestPhase.Transitioned:
|
case RustSdkCryptoJs.VerificationRequestPhase.Transitioned:
|
||||||
return VerificationPhase.Started;
|
return VerificationPhase.Started;
|
||||||
case RustSdkCryptoJs.VerificationRequestPhase.Done:
|
case RustSdkCryptoJs.VerificationRequestPhase.Done:
|
||||||
@@ -137,7 +153,7 @@ export class RustVerificationRequest
|
|||||||
* the remote echo which causes a transition to {@link VerificationPhase.Ready}.
|
* the remote echo which causes a transition to {@link VerificationPhase.Ready}.
|
||||||
*/
|
*/
|
||||||
public get accepting(): boolean {
|
public get accepting(): boolean {
|
||||||
throw new Error("not implemented");
|
return this._accepting;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,7 +161,7 @@ export class RustVerificationRequest
|
|||||||
* the remote echo which causes a transition to {@link VerificationPhase.Cancelled}).
|
* the remote echo which causes a transition to {@link VerificationPhase.Cancelled}).
|
||||||
*/
|
*/
|
||||||
public get declining(): boolean {
|
public get declining(): boolean {
|
||||||
throw new Error("not implemented");
|
return this._cancelling;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -198,8 +214,28 @@ export class RustVerificationRequest
|
|||||||
*
|
*
|
||||||
* @returns Promise which resolves when the event has been sent.
|
* @returns Promise which resolves when the event has been sent.
|
||||||
*/
|
*/
|
||||||
public accept(): Promise<void> {
|
public async accept(): Promise<void> {
|
||||||
throw new Error("not implemented");
|
if (this.inner.phase() !== RustSdkCryptoJs.VerificationRequestPhase.Requested || this._accepting) {
|
||||||
|
throw new Error(`Cannot accept a verification request in phase ${this.phase}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._accepting = true;
|
||||||
|
try {
|
||||||
|
const req: undefined | OutgoingRequest =
|
||||||
|
this.supportedVerificationMethods === undefined
|
||||||
|
? this.inner.accept()
|
||||||
|
: this.inner.acceptWithMethods(
|
||||||
|
this.supportedVerificationMethods.map(verificationMethodIdentifierToMethod),
|
||||||
|
);
|
||||||
|
if (req) {
|
||||||
|
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this._accepting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// phase may have changed, so emit a 'change' event
|
||||||
|
this.emit(VerificationRequestEvent.Change);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -211,9 +247,19 @@ export class RustVerificationRequest
|
|||||||
* @returns Promise which resolves when the event has been sent.
|
* @returns Promise which resolves when the event has been sent.
|
||||||
*/
|
*/
|
||||||
public async cancel(params?: { reason?: string; code?: string }): Promise<void> {
|
public async cancel(params?: { reason?: string; code?: string }): Promise<void> {
|
||||||
const req: undefined | OutgoingRequest = this.inner.cancel();
|
if (this._cancelling) {
|
||||||
if (req) {
|
// already cancelling; do nothing
|
||||||
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cancelling = true;
|
||||||
|
try {
|
||||||
|
const req: undefined | OutgoingRequest = this.inner.cancel();
|
||||||
|
if (req) {
|
||||||
|
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this._cancelling = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1427,9 +1427,9 @@
|
|||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
|
|
||||||
"@matrix-org/matrix-sdk-crypto-js@^0.1.0-alpha.11":
|
"@matrix-org/matrix-sdk-crypto-js@^0.1.0-alpha.11":
|
||||||
version "0.1.0-alpha.11"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.11.tgz#24d705318c3159ef7dbe43bca464ac2bdd11e45d"
|
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0.tgz#766580036d4df12120ded223e13b5640e77db136"
|
||||||
integrity sha512-HD3rskPkqrUUSaKzGLg97k/bN+OZrkcX7ODB/pNBs/jqq+/A0wDKqsszJotzFwsQcDPpWn78BmMyvBo4tLxKjw==
|
integrity sha512-ra/bcFdleC1iRNms2I96UXA0NvQYWpMsHrV5EfJRS7qV1PtnQNvgsvMfjMbkx8QT2ErEmIhsvB5fPCpfp8BSuw==
|
||||||
|
|
||||||
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz":
|
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz":
|
||||||
version "3.2.14"
|
version "3.2.14"
|
||||||
|
Reference in New Issue
Block a user