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

Add QR code buffer generation to VerificationRequest (from react-sdk)

Doing this so later we can keep the other user master signing key on it
so we can make sure we cross-sign that one once we decide to sign.
This commit is contained in:
Bruno Windels
2020-03-31 13:50:43 +02:00
parent 934ed37fdc
commit 725976d472
2 changed files with 131 additions and 0 deletions

View File

@@ -25,6 +25,7 @@ import {
newKeyMismatchError, newKeyMismatchError,
newUserMismatchError, newUserMismatchError,
} from './Error'; } from './Error';
import {decodeBase64} from "../olmlib";
export const SHOW_QR_CODE_METHOD = "m.qr_code.show.v1"; export const SHOW_QR_CODE_METHOD = "m.qr_code.show.v1";
export const SCAN_QR_CODE_METHOD = "m.qr_code.scan.v1"; export const SCAN_QR_CODE_METHOD = "m.qr_code.scan.v1";
@@ -111,3 +112,113 @@ export class ReciprocateQRCode extends Base {
}); });
} }
} }
const CODE_VERSION = 0x02; // the version of binary QR codes we support
const BINARY_PREFIX = "MATRIX"; // ASCII, used to prefix the binary format
const MODE_VERIFY_OTHER_USER = 0x00; // Verifying someone who isn't us
const MODE_VERIFY_SELF_TRUSTED = 0x01; // We trust the master key
const MODE_VERIFY_SELF_UNTRUSTED = 0x02; // We do not trust the master key
export class QRCodeData {
constructor(request, client) {
const qrData = QRCodeData._generateQrData(request, client);
this._buffer = QRCodeData._generateBuffer(qrData);
}
get buffer() {
return this._buffer;
}
static _generateQrData(request, client) {
const myUserId = client.getUserId();
const otherUserId = request.otherUserId;
let mode = MODE_VERIFY_OTHER_USER;
if (myUserId === otherUserId) {
// Mode changes depending on whether or not we trust the master cross signing key
const myTrust = client.checkUserTrust(myUserId);
if (myTrust.isCrossSigningVerified()) {
mode = MODE_VERIFY_SELF_TRUSTED;
} else {
mode = MODE_VERIFY_SELF_UNTRUSTED;
}
}
const transactionId = request.channel.transactionId;
const qrData = {
prefix: BINARY_PREFIX,
version: CODE_VERSION,
mode,
transactionId,
firstKeyB64: '', // worked out shortly
secondKeyB64: '', // worked out shortly
secretB64: request.encodedSharedSecret,
};
const myCrossSigningInfo = client.getStoredCrossSigningForUser(myUserId);
const myDevices = client.getStoredDevicesForUser(myUserId) || [];
if (mode === MODE_VERIFY_OTHER_USER) {
// First key is our master cross signing key
qrData.firstKeyB64 = myCrossSigningInfo.getId("master");
// Second key is the other user's master cross signing key
const otherUserCrossSigningInfo =
client.getStoredCrossSigningForUser(otherUserId);
qrData.secondKeyB64 = otherUserCrossSigningInfo.getId("master");
} else if (mode === MODE_VERIFY_SELF_TRUSTED) {
// First key is our master cross signing key
qrData.firstKeyB64 = myCrossSigningInfo.getId("master");
// Second key is the other device's device key
const otherDevice = request.targetDevice;
const otherDeviceId = otherDevice ? otherDevice.deviceId : null;
const device = myDevices.find(d => d.deviceId === otherDeviceId);
qrData.secondKeyB64 = device.getFingerprint();
} else if (mode === MODE_VERIFY_SELF_UNTRUSTED) {
// First key is our device's key
qrData.firstKeyB64 = client.getDeviceEd25519Key();
// Second key is what we think our master cross signing key is
qrData.secondKeyB64 = myCrossSigningInfo.getId("master");
}
return qrData;
}
static _generateBuffer(qrData) {
let buf = Buffer.alloc(0); // we'll concat our way through life
const appendByte = (b: number) => {
const tmpBuf = Buffer.from([b]);
buf = Buffer.concat([buf, tmpBuf]);
};
const appendInt = (i: number) => {
const tmpBuf = Buffer.alloc(2);
tmpBuf.writeInt16BE(i, 0);
buf = Buffer.concat([buf, tmpBuf]);
};
const appendStr = (s: string, enc: string, withLengthPrefix = true) => {
const tmpBuf = Buffer.from(s, enc);
if (withLengthPrefix) appendInt(tmpBuf.byteLength);
buf = Buffer.concat([buf, tmpBuf]);
};
const appendEncBase64 = (b64: string) => {
const b = decodeBase64(b64);
const tmpBuf = Buffer.from(b);
buf = Buffer.concat([buf, tmpBuf]);
};
// Actually build the buffer for the QR code
appendStr(qrData.prefix, "ascii", false);
appendByte(qrData.version);
appendByte(qrData.mode);
appendStr(qrData.transactionId, "utf-8");
appendEncBase64(qrData.firstKeyB64);
appendEncBase64(qrData.secondKeyB64);
appendEncBase64(qrData.secretB64);
return buf;
}
}

View File

@@ -24,6 +24,7 @@ import {
newUnknownMethodError, newUnknownMethodError,
} from "../Error"; } from "../Error";
import * as olmlib from "../../olmlib"; import * as olmlib from "../../olmlib";
import {QRCodeData, SCAN_QR_CODE_METHOD} from "../QRCode";
// the recommended amount of time before a verification request // the recommended amount of time before a verification request
// should be (automatically) cancelled without user interaction // should be (automatically) cancelled without user interaction
@@ -74,6 +75,11 @@ export class VerificationRequest extends EventEmitter {
this._accepting = false; this._accepting = false;
this._declining = false; this._declining = false;
this._verifierHasFinished = false; this._verifierHasFinished = false;
// we keep a copy of the QR Code data (including other user master key) around
// for QR reciprocate verification, to protect against
// cross-signing identity reset between the .ready and .start event
// and signing the wrong key after .start
this._qrCodeData = null;
} }
/** /**
@@ -201,6 +207,12 @@ export class VerificationRequest extends EventEmitter {
this._phase !== PHASE_CANCELLED; this._phase !== PHASE_CANCELLED;
} }
/** Only set after a .ready if the other party can scan a QR code */
get qrCodeData() {
// TODO, merge encodedSharedSecret in here as well
return this._qrCodeData;
}
/** Checks whether the other party supports a given verification method. /** Checks whether the other party supports a given verification method.
* This is useful when setting up the QR code UI, as it is somewhat asymmetrical: * This is useful when setting up the QR code UI, as it is somewhat asymmetrical:
* if the other party supports SCAN_QR, we should show a QR code in the UI, and vice versa. * if the other party supports SCAN_QR, we should show a QR code in the UI, and vice versa.
@@ -554,6 +566,14 @@ export class VerificationRequest extends EventEmitter {
} }
} }
} }
if (phase === PHASE_READY) {
const shouldGenerateQrCode =
this.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
if (shouldGenerateQrCode) {
// TODO: we should only do this for live events
this._qrCodeData = new QRCodeData(this, this._client);
}
}
// create verifier // create verifier
if (phase === PHASE_STARTED) { if (phase === PHASE_STARTED) {
const {method} = event.getContent(); const {method} = event.getContent();