Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QR code verification #1155

Merged
merged 7 commits into from
Jan 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion spec/unit/crypto/verification/request.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import "../../../olm-loader";
import {verificationMethods} from "../../../../src/crypto";
import {logger} from "../../../../src/logger";
import {SAS} from "../../../../src/crypto/verification/SAS";
import {makeTestClients} from './util';
import {makeTestClients, setupWebcrypto, teardownWebcrypto} from './util';

const Olm = global.Olm;

Expand All @@ -31,9 +31,14 @@ describe("verification request integration tests with crypto layer", function()
}

beforeAll(function() {
setupWebcrypto();
return Olm.init();
});

afterAll(() => {
teardownWebcrypto();
});

it("should request and accept a verification", async function() {
const [alice, bob] = await makeTestClients(
[
Expand Down
7 changes: 6 additions & 1 deletion spec/unit/crypto/verification/sas.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import "../../../olm-loader";
import {makeTestClients} from './util';
import {makeTestClients, setupWebcrypto, teardownWebcrypto} from './util';
import {MatrixEvent} from "../../../../src/models/event";
import {SAS} from "../../../../src/crypto/verification/SAS";
import {DeviceInfo} from "../../../../src/crypto/deviceinfo";
Expand All @@ -35,9 +35,14 @@ describe("SAS verification", function() {
}

beforeAll(function() {
setupWebcrypto();
return Olm.init();
});

afterAll(() => {
teardownWebcrypto();
});

it("should error on an unexpected event", async function() {
const sas = new SAS({}, "@alice:example.com", "ABCDEFG");
sas.handleEvent(new MatrixEvent({
Expand Down
13 changes: 13 additions & 0 deletions spec/unit/crypto/verification/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.

import {TestClient} from '../../../TestClient';
import {MatrixEvent} from "../../../../src/models/event";
import nodeCrypto from "crypto";

export async function makeTestClients(userInfos, options) {
const clients = [];
Expand Down Expand Up @@ -102,3 +103,15 @@ export async function makeTestClients(userInfos, options) {

return clients;
}

export function setupWebcrypto() {
global.crypto = {
getRandomValues: (buf) => {
return nodeCrypto.randomFillSync(buf);
},
};
}

export function teardownWebcrypto() {
global.crypto = undefined;
}
10 changes: 10 additions & 0 deletions spec/unit/crypto/verification/verification_request.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {InRoomChannel} from "../../../../src/crypto/verification/request/InRoomC
import {ToDeviceChannel} from
"../../../../src/crypto/verification/request/ToDeviceChannel";
import {MatrixEvent} from "../../../../src/models/event";
import {setupWebcrypto, teardownWebcrypto} from "./util";

function makeMockClient(userId, deviceId) {
let counter = 1;
Expand Down Expand Up @@ -115,6 +116,15 @@ async function distributeEvent(ownRequest, theirRequest, event) {
}

describe("verification request unit tests", function() {

beforeAll(function() {
setupWebcrypto();
});

afterAll(() => {
teardownWebcrypto();
});

it("transition from UNSENT to DONE through happy path", async function() {
const alice = makeMockClient("@alice:matrix.tld", "device1");
const bob = makeMockClient("@bob:matrix.tld", "device1");
Expand Down
27 changes: 27 additions & 0 deletions src/crypto/verification/request/VerificationRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
newUnexpectedMessageError,
newUnknownMethodError,
} from "../Error";
import * as olmlib from "../../olmlib";

// the recommended amount of time before a verification request
// should be (automatically) cancelled without user interaction
Expand Down Expand Up @@ -69,6 +70,7 @@ export class VerificationRequest extends EventEmitter {
this._eventsByThem = new Map();
this._observeOnly = false;
this._timeoutTimer = null;
this._sharedSecret = null; // used for QR codes
}

/**
Expand Down Expand Up @@ -157,6 +159,14 @@ export class VerificationRequest extends EventEmitter {
return 0;
}

/**
* The key verification request event.
* @returns {MatrixEvent} The request event, or falsey if not found.
*/
get requestEvent() {
return this._getEventByEither(REQUEST_TYPE);
}

/** current phase of the request. Some properties might only be defined in a current phase. */
get phase() {
return this._phase;
Expand Down Expand Up @@ -243,6 +253,15 @@ export class VerificationRequest extends EventEmitter {
return this._observeOnly;
}

/**
* The unpadded base64 encoded shared secret. Primarily used for QR code
* verification.
*/
get encodedSharedSecret() {
if (!this._sharedSecret) this._generateSharedSecret();
return this._sharedSecret;
}

/* 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.
Expand Down Expand Up @@ -281,6 +300,7 @@ export class VerificationRequest extends EventEmitter {
if (!this.observeOnly && this._phase === PHASE_UNSENT) {
const methods = [...this._verificationMethods.keys()];
await this.channel.send(REQUEST_TYPE, {methods});
this._generateSharedSecret();
}
}

Expand Down Expand Up @@ -309,9 +329,16 @@ export class VerificationRequest extends EventEmitter {
if (!this.observeOnly && this.phase === PHASE_REQUESTED && !this.initiatedByMe) {
const methods = [...this._verificationMethods.keys()];
await this.channel.send(READY_TYPE, {methods});
this._generateSharedSecret();
}
}

_generateSharedSecret() {
const secretBytes = new Uint8Array(32); // 256bits
global.crypto.getRandomValues(secretBytes);
this._sharedSecret = olmlib.encodeBase64(secretBytes);
}

/**
* Can be used to listen for state changes until the callback returns true.
* @param {Function} fn callback to evaluate whether the request is in the desired state.
Expand Down