diff --git a/src/crypto/index.js b/src/crypto/index.js index d94ba0aa2..aa46f8159 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -1550,6 +1550,7 @@ Crypto.prototype._requestVerificationWithChannel = async function( } else { map.set(name, method); } + return map; }, new Map()); } const request = new VerificationRequest( @@ -2469,6 +2470,8 @@ Crypto.prototype._onKeyVerificationMessage = function(event) { * @param {module:models/event.MatrixEvent} event the timeline event */ Crypto.prototype._onTimelineEvent = function(event) { + // TODO: we still need a request object for past requests, so we can show it in the timeline + // validation now excludes old requests if (!InRoomChannel.validateEvent(event, this._baseApis)) { return; } @@ -2503,7 +2506,6 @@ Crypto.prototype._handleVerificationEvent = async function( if (!request) { return; } - event.setVerificationRequest(request); isNewRequest = true; if (!requestsByTxnId) { requestsByTxnId = new Map(); @@ -2511,6 +2513,7 @@ Crypto.prototype._handleVerificationEvent = async function( } requestsByTxnId.set(transactionId, request); } + event.setVerificationRequest(request); try { const hadVerifier = !!request.verifier; await request.channel.handleEvent(event, request); diff --git a/src/crypto/verification/request/InRoomChannel.js b/src/crypto/verification/request/InRoomChannel.js index 94882f200..b476fbb31 100644 --- a/src/crypto/verification/request/InRoomChannel.js +++ b/src/crypto/verification/request/InRoomChannel.js @@ -46,6 +46,10 @@ export default class InRoomChannel { return true; } + get roomId() { + return this._roomId; + } + /** The transaction id generated/used by this verification channel */ get transactionId() { return this._requestEventId; @@ -96,17 +100,20 @@ export default class InRoomChannel { static validateEvent(event, client) { const txnId = InRoomChannel.getTransactionId(event); if (typeof txnId !== "string" || txnId.length === 0) { + console.log("InRoomChannel: validateEvent: no valid txnId", type, txnId); return false; } const type = InRoomChannel.getEventType(event); const content = event.getContent(); if (type === REQUEST_TYPE) { - if (typeof content.to !== "string" || !content.to.length) { + if (!content || typeof content.to !== "string" || !content.to.length) { + console.log("InRoomChannel: validateEvent: no valid to", type, content.to); return false; } const ownUserId = client.getUserId(); // ignore requests that are not direct to or sent by the syncing user if (event.getSender() !== ownUserId && content.to !== ownUserId) { + console.log("InRoomChannel: validateEvent: not directed or sent my me", type, event.getSender(), content.to); return false; } } diff --git a/src/crypto/verification/request/VerificationRequest.js b/src/crypto/verification/request/VerificationRequest.js index 1cd5aec87..7b786735b 100644 --- a/src/crypto/verification/request/VerificationRequest.js +++ b/src/crypto/verification/request/VerificationRequest.js @@ -66,9 +66,12 @@ export default class VerificationRequest extends EventEmitter { this._commonMethods = []; this._setPhase(PHASE_UNSENT, false); this._requestEvent = null; + // TODO: this can also be ourselves, we need to fish .to out of content on a .request this._otherUserId = userId; this._initiatedByMe = null; this._startTimestamp = null; + this._cancellingUserId = null; + this._readyEvent = null; } /** @@ -83,12 +86,18 @@ export default class VerificationRequest extends EventEmitter { static validateEvent(type, event, timestamp, client) { const content = event.getContent(); + if (!content) { + console.log("VerificationRequest: validateEvent: no content", type); + } + if (!type.startsWith(EVENT_PREFIX)) { + console.log("VerificationRequest: validateEvent: fail because type doesnt start with " + EVENT_PREFIX, type); return false; } if (type === REQUEST_TYPE || type === READY_TYPE) { if (!Array.isArray(content.methods)) { + console.log("VerificationRequest: validateEvent: fail because methods", type); return false; } } @@ -97,6 +106,7 @@ export default class VerificationRequest extends EventEmitter { if (typeof content.from_device !== "string" || content.from_device.length === 0 ) { + console.log("VerificationRequest: validateEvent: fail because from_device", type); return false; } } @@ -106,7 +116,9 @@ export default class VerificationRequest extends EventEmitter { const elapsed = Date.now() - timestamp; // ignore if event is too far in the past or too far in the future if (elapsed > (VERIFICATION_REQUEST_TIMEOUT - VERIFICATION_REQUEST_MARGIN) || - elapsed < -(VERIFICATION_REQUEST_TIMEOUT / 2)) { + elapsed < -(VERIFICATION_REQUEST_TIMEOUT / 2) + ) { + console.log("VerificationRequest: validateEvent: verification event to far in future or past", type, elapsed); logger.log("received verification that is too old or from the future"); return false; } @@ -199,6 +211,14 @@ export default class VerificationRequest extends EventEmitter { } } + /** + * the id of the user that cancelled the request, + * only defined when phase is PHASE_CANCELLED + */ + get cancellingUserId() { + return this._cancellingUserId; + } + /* Start the key verification, creating a verifier and sending a .start event. * If no previous events have been sent, pass in `targetDevice` to set who to direct this request to. * @param {string} method the name of the verification method to use. @@ -249,6 +269,7 @@ export default class VerificationRequest extends EventEmitter { if (this._verifier) { return this._verifier.cancel(errorFactory(code, reason)); } else { + this._cancellingUserId = this._client.getUserId(); this._setPhase(PHASE_CANCELLED, false); await this.channel.send(CANCEL_TYPE, {code, reason}); } @@ -262,8 +283,8 @@ export default class VerificationRequest extends EventEmitter { */ async accept() { if (this.phase === PHASE_REQUESTED && !this.initiatedByMe) { - const methods = [...this._verificationMethods.keys()]; this._setPhase(PHASE_READY, false); + const methods = [...this._verificationMethods.keys()]; await this.channel.send(READY_TYPE, {methods}); this.emit("change"); } @@ -322,7 +343,7 @@ export default class VerificationRequest extends EventEmitter { if (type === REQUEST_TYPE) { await this._handleRequest(content, event); } else if (type === READY_TYPE) { - await this._handleReady(content); + await this._handleReady(event); } else if (type === START_TYPE) { await this._handleStart(content, event); } @@ -335,7 +356,7 @@ export default class VerificationRequest extends EventEmitter { } if (type === CANCEL_TYPE) { - this._handleCancel(); + this._handleCancel(event); } else if (type === DONE_TYPE) { this._handleDone(); } @@ -349,19 +370,22 @@ export default class VerificationRequest extends EventEmitter { this._initiatedByMe = this._wasSentByMe(event); this._setPhase(PHASE_REQUESTED); } else if (this._phase !== PHASE_REQUESTED) { - logger.warn("Ignoring flagged verification request from " + + logger.warn("Cancelling, unexpected .request verification event from " + event.getSender()); await this.cancel(errorFromEvent(newUnexpectedMessageError())); } } - async _handleReady(content) { - if (this._phase === PHASE_REQUESTED) { + async _handleReady(event) { + const content = event.getContent(); + console.log("handling ready event", content, this._phase); + if (this.phase === PHASE_REQUESTED || this.phase === PHASE_READY) { + this._readyEvent = event; const otherMethods = content.methods; this._commonMethods = this._filterMethods(otherMethods); this._setPhase(PHASE_READY); } else { - logger.warn("Ignoring flagged verification ready event from " + + logger.warn("Cancelling, unexpected .ready verification event from " + event.getSender()); await this.cancel(errorFromEvent(newUnexpectedMessageError())); } @@ -385,7 +409,9 @@ export default class VerificationRequest extends EventEmitter { if (this.phase === PHASE_UNSENT) { this._initiatedByMe = this._wasSentByMe(event); } - this._verifier = this._createVerifier(method, event); + if (!this._verifier) { + this._verifier = this._createVerifier(method, event); + } this._setPhase(PHASE_STARTED); } } @@ -409,8 +435,9 @@ export default class VerificationRequest extends EventEmitter { } } - _handleCancel() { + _handleCancel(event = null) { if (this._phase !== PHASE_CANCELLED) { + this._cancellingUserId = event ? event.getSender() : this._client.getUserId(); this._setPhase(PHASE_CANCELLED); } } @@ -450,6 +477,8 @@ export default class VerificationRequest extends EventEmitter { let targetEvent; if (startEvent && !this._wasSentByMe(startEvent)) { targetEvent = startEvent; + } else if (this._readyEvent && !this._wasSentByMe(this._readyEvent)) { + targetEvent = this._readyEvent; } else if (this._requestEvent && !this._wasSentByMe(this._requestEvent)) { targetEvent = this._requestEvent; } else { @@ -468,7 +497,7 @@ export default class VerificationRequest extends EventEmitter { return methodNames.filter(m => this._verificationMethods.has(m)); } - // only for .request and .start + // only for .request, .ready or .start _wasSentByMe(event) { if (event.getSender() !== this._client.getUserId()) { return false;