1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-26 17:03:12 +03:00

Add untested reciprocate function

This commit is contained in:
Travis Ralston
2020-01-27 11:41:52 -07:00
parent 2d081f2c19
commit 419693023f
2 changed files with 77 additions and 32 deletions

View File

@@ -41,7 +41,7 @@ import {
import {SECRET_STORAGE_ALGORITHM_V1, SecretStorage} from './SecretStorage'; import {SECRET_STORAGE_ALGORITHM_V1, SecretStorage} from './SecretStorage';
import {OutgoingRoomKeyRequestManager} from './OutgoingRoomKeyRequestManager'; import {OutgoingRoomKeyRequestManager} from './OutgoingRoomKeyRequestManager';
import {IndexedDBCryptoStore} from './store/indexeddb-crypto-store'; import {IndexedDBCryptoStore} from './store/indexeddb-crypto-store';
import {ScanQRCode, ShowQRCode} from './verification/QRCode'; import {ReciprocateQRCode, ScanQRCode, ShowQRCode} from './verification/QRCode';
import {SAS} from './verification/SAS'; import {SAS} from './verification/SAS';
import {keyFromPassphrase} from './key_passphrase'; import {keyFromPassphrase} from './key_passphrase';
import {encodeRecoveryKey} from './recoverykey'; import {encodeRecoveryKey} from './recoverykey';
@@ -55,6 +55,7 @@ const DeviceVerification = DeviceInfo.DeviceVerification;
const defaultVerificationMethods = { const defaultVerificationMethods = {
[ScanQRCode.NAME]: ScanQRCode, [ScanQRCode.NAME]: ScanQRCode,
[ShowQRCode.NAME]: ShowQRCode, [ShowQRCode.NAME]: ShowQRCode,
[ReciprocateQRCode.NAME]: ReciprocateQRCode,
[SAS.NAME]: SAS, [SAS.NAME]: SAS,
}; };
@@ -64,6 +65,7 @@ const defaultVerificationMethods = {
export const verificationMethods = { export const verificationMethods = {
QR_CODE_SCAN: ScanQRCode.NAME, QR_CODE_SCAN: ScanQRCode.NAME,
QR_CODE_SHOW: ShowQRCode.NAME, QR_CODE_SHOW: ShowQRCode.NAME,
RECIPROCATE_QR_CODE: ReciprocateQRCode.NAME,
SAS: SAS.NAME, SAS: SAS.NAME,
}; };

View File

@@ -30,7 +30,6 @@ import {
import * as qs from "qs"; import * as qs from "qs";
const MATRIXTO_REGEXP = /^(?:https?:\/\/)?(?:www\.)?matrix\.to\/#\/([#@!+][^?]+)\?(.+)$/; const MATRIXTO_REGEXP = /^(?:https?:\/\/)?(?:www\.)?matrix\.to\/#\/([#@!+][^?]+)\?(.+)$/;
const KEY_REGEXP = /^key_([^:]+:.+)$/;
const newQRCodeError = errorFactory("m.qr_code.invalid", "Invalid QR code"); const newQRCodeError = errorFactory("m.qr_code.invalid", "Invalid QR code");
@@ -83,38 +82,34 @@ export class ScanQRCode extends Base {
cancel: () => reject(newUserCancelledError()), cancel: () => reject(newUserCancelledError()),
}); });
}); });
const {action, secret, otherUserKey, keys, targetUserId} = ReciprocateQRCode.splitUrl(code);
}
}
const match = code.match(MATRIXTO_REGEXP); ScanQRCode.NAME = "m.qr_code.scan.v1";
let deviceId;
const keys = {}; /**
if (!match) { * @class crypto/verification/QRCode/ReciprocateQRCode
throw newQRCodeError(); * @extends {module:crypto/verification/Base}
} */
const userId = match[1]; export class ReciprocateQRCode extends Base {
const params = match[2].split("&").map( static factory(...args) {
(x) => x.split("=", 2).map(decodeURIComponent), return new ReciprocateQRCode(...args);
);
let action;
for (const [name, value] of params) {
if (name === "device") {
deviceId = value;
} else if (name === "action") {
action = value;
} else {
const keyMatch = name.match(KEY_REGEXP);
if (keyMatch) {
keys[keyMatch[1]] = value;
}
}
}
if (!deviceId || action !== "verify" || Object.keys(keys).length === 0) {
throw newQRCodeError();
} }
async _doVerification() {
const code = await new Promise((resolve, reject) => {
this.emit("scan", {
done: resolve,
cancel: () => reject(newUserCancelledError()),
});
});
const {secret, otherUserKey, keys, targetUserId} = ReciprocateQRCode.splitUrl(code);
if (!this.userId) { if (!this.userId) {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
this.emit("confirm_user_id", { this.emit("confirm_user_id", {
userId: userId, userId: targetUserId,
confirm: resolve, confirm: resolve,
cancel: () => reject(newUserMismatchError()), cancel: () => reject(newUserMismatchError()),
}); });
@@ -122,16 +117,64 @@ export class ScanQRCode extends Base {
} else if (this.userId !== userId) { } else if (this.userId !== userId) {
throw newUserMismatchError({ throw newUserMismatchError({
expected: this.userId, expected: this.userId,
actual: userId, actual: targetUserId,
}); });
} }
await this._verifyKeys(userId, keys, (keyId, device, key) => { const crossSigningInfo = this._baseApis.getStoredCrossSigningInfo(targetUserId);
if (!crossSigningInfo) throw new Error("Missing cross signing info for user"); // this shouldn't happen by now
if (crossSigningInfo.getId("master") !== otherUserKey) {
throw newKeyMismatchError();
}
if (secret !== this.request.encodedSharedSecret) {
throw newQRCodeError();
}
// Verify our own keys that were sent in this code too
await this._verifyKeys(this._baseApis.getUserId(), keys, (keyId, device, key) => {
if (device.keys[keyId] !== key) {
throw newKeyMismatchError();
}
});
await this._verifyKeys(targetUserId, [otherUserKey, otherUserKey], (keyId, device, key) => {
if (device.keys[keyId] !== key) { if (device.keys[keyId] !== key) {
throw newKeyMismatchError(); throw newKeyMismatchError();
} }
}); });
} }
static splitUrl(code) {
const match = code.match(MATRIXTO_REGEXP);
const keys = {};
if (!match) {
throw newQRCodeError();
}
const targetUserId = match[1];
const params = match[2].split("&").map(
(x) => x.split("=", 2).map(decodeURIComponent),
);
let action;
let otherUserKey;
let secret;
for (const [name, value] of params) {
if (name === "action") {
action = value;
} else if (name.startsWith("key_")) {
keys[name.substring("key_".length)] = value;
} else if (name === "other_user_key") {
otherUserKey = value;
} else if (name === "secret") {
secret = value;
}
}
if (!secret || !otherUserKey || action !== "verify" || Object.keys(keys).length === 0) {
throw newQRCodeError();
}
return {action, secret, otherUserKey, keys, targetUserId};
}
} }
ScanQRCode.NAME = "m.qr_code.scan.v1"; ReciprocateQRCode.NAME = "m.reciprocate.v1";