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

Cross Signing Support #832

Merged
merged 103 commits into from
Nov 15, 2019
Merged
Show file tree
Hide file tree
Changes from 98 commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
2b54f44
Add cross signing key creation into key backup
dbkr Jan 30, 2019
02d4dcb
Store SSK & USK in crypto store
dbkr Jan 31, 2019
1f77cc6
Cross sign the current device with the SSK
dbkr Jan 31, 2019
1d58a64
Track SSKs for users
dbkr Feb 1, 2019
910d0ec
Sign & trust the key backup from the SSK
dbkr Feb 1, 2019
7195365
Update package-lock.json
dbkr Feb 1, 2019
7dedcb8
Lint
dbkr Feb 1, 2019
c808253
Always track your own devices
dbkr Feb 1, 2019
5500f0d
Re-track own device list
dbkr Feb 1, 2019
1b82dff
Merge remote-tracking branch 'origin/develop' into dbkr/cross_signing
dbkr Feb 1, 2019
b3513dc
Make linting rules more consistent
dbkr Feb 5, 2019
7f5584e
All the linting
dbkr Feb 5, 2019
e54f717
Olm pre2 for cross-signing
dbkr Feb 5, 2019
32814d1
Merge branch 'develop' into dbkr/cross_signing
uhoreg Apr 3, 2019
ec2f07e
add methods for signing and checking users and devices with cross-sig…
uhoreg May 3, 2019
ae71f41
add missing files
uhoreg May 3, 2019
b0275af
remove some debugging lines
uhoreg May 4, 2019
405451d
complete some more unit tests
uhoreg May 4, 2019
193ad9e
use 3 keys for cross-signing
uhoreg May 23, 2019
53804ca
save cross-signing keys from sync and verify new keys for user
uhoreg May 29, 2019
609ee66
use the right path for logger
uhoreg May 29, 2019
941d871
fix check for empty cross-signing repsonse
uhoreg May 29, 2019
936eef1
minor fixes to tests
uhoreg May 29, 2019
95131c7
add test for syncing trust on another user
uhoreg May 29, 2019
dc971b9
add missing semicolon
uhoreg Jun 4, 2019
4a9a1b4
initial implementation of secret storage and sharing
uhoreg Jun 4, 2019
6a77df7
Merge branch 'develop' into dbkr/cross_signing
uhoreg Jun 5, 2019
5f539aa
Merge branch 'develop' into dbkr/cross_signing
uhoreg Jun 5, 2019
0c714ba
some cleanups
uhoreg Jun 5, 2019
6f6e7ea
verify cross-signing key with SAS
uhoreg Jun 5, 2019
98815ff
allow http request stub to ignore unhandled syncs
uhoreg Jun 12, 2019
4c6fa89
various cross-signing fixes and improvements
uhoreg Jun 12, 2019
5bcbe76
cleanups and a lot more docs
uhoreg Jun 15, 2019
07c2e34
Merge branch 'develop' into dbkr/cross_signing
uhoreg Jun 15, 2019
1cae5e8
fix unit tests to match event name changes
uhoreg Jun 28, 2019
4356603
save public part of cross-signing keys
uhoreg Jun 28, 2019
c5caf8f
sign backups with master key
uhoreg Jul 3, 2019
46a8486
rename m.secrets.share to m.secrets.send to agree with latest MSC
uhoreg Jul 3, 2019
6cd09c6
pksign was moved to olmlib
uhoreg Jul 3, 2019
8d1d657
add unit test for backups signed by cross-signing key
uhoreg Jul 3, 2019
b008041
obsolete todo
uhoreg Jul 4, 2019
761f22b
minor cleanups
uhoreg Jul 8, 2019
7f8b9de
offer to upgrade device verifications to cross-signing
uhoreg Jul 8, 2019
f3ec976
update to follow latest MSC
uhoreg Aug 27, 2019
8cad116
Make tests pass
dbkr Oct 28, 2019
3bec28b
make other tests pass
dbkr Oct 28, 2019
de1b545
lint
dbkr Oct 28, 2019
e92d2bd
Fix test again
dbkr Oct 28, 2019
3e2d845
Merge remote-tracking branch 'origin/develop' into dbkr/cross_signing
dbkr Oct 28, 2019
49588da
Fix more tests
dbkr Oct 29, 2019
74b649c
Typo
dbkr Nov 1, 2019
a571624
Typo
dbkr Nov 1, 2019
f3073e1
Space
dbkr Nov 1, 2019
a34758f
Convert event interface to callbacks
dbkr Nov 7, 2019
fabfe16
lint
dbkr Nov 7, 2019
1262702
Convert sas verification test to callbacks
dbkr Nov 7, 2019
03fe4af
lint
dbkr Nov 7, 2019
3a98327
add comments
dbkr Nov 7, 2019
6f8d9c4
Rename getPrivateKeys to getCrossSigningKeys
dbkr Nov 11, 2019
a98e696
Missed bits of callback renaming
dbkr Nov 11, 2019
4c651c1
Convert secrets events to callbacks too
dbkr Nov 11, 2019
9bc185d
Fix what was probablyt a c+p fail
dbkr Nov 12, 2019
c97a87d
Throw if an unknown key is specified
dbkr Nov 12, 2019
26aa3d3
Support default keys
dbkr Nov 13, 2019
d12c56a
lint
dbkr Nov 13, 2019
1798f39
Make setDeafultKeyId wait for event
dbkr Nov 13, 2019
7218e31
Sign & verify SSSS keys
dbkr Nov 13, 2019
693c749
lint
dbkr Nov 13, 2019
d5d8032
Camelcase event names
dbkr Nov 14, 2019
d9d6530
More s/cross-signing/crossSigning/
dbkr Nov 14, 2019
0048cbe
Mark cross siging / SSSS APIs as unstable
dbkr Nov 14, 2019
e10c17c
Use official name for SSSS
dbkr Nov 14, 2019
291133b
Fix comment
dbkr Nov 14, 2019
2cd748b
Add matrix foundation copyright
dbkr Nov 14, 2019
69ecf3b
jsdoc formatting
dbkr Nov 14, 2019
2a7b283
remove unused function
dbkr Nov 14, 2019
686a7a4
Remove outdated comment
dbkr Nov 14, 2019
7ca09ad
tariling space
dbkr Nov 14, 2019
be9b7a0
Remove getPublicKey
dbkr Nov 14, 2019
5937185
Assert usage of setDeviceVerification for cross-signing keys
dbkr Nov 14, 2019
ce2d1d6
Don't emit event here, as per comment
dbkr Nov 14, 2019
e541b96
Change check{User|Device}Trust interfaces
dbkr Nov 15, 2019
c3215d5
Switch the CroosSigningLevel constants
dbkr Nov 15, 2019
6f42824
Typo
dbkr Nov 15, 2019
4089349
copy jsdoc to internal methods
dbkr Nov 15, 2019
545ebf8
Move Crypto.prototype.init back to its rightful place
dbkr Nov 15, 2019
fe01024
Why is 'cross-signing' so hard to type?
dbkr Nov 15, 2019
f5a5f5e
Update yarn.lock
dbkr Nov 15, 2019
d37ed9f
lint
dbkr Nov 15, 2019
f84ec09
backticks in jsdoc
dbkr Nov 15, 2019
f2f205f
Typo
dbkr Nov 15, 2019
86e0f49
c+p fail
dbkr Nov 15, 2019
00b571a
c+p fail
dbkr Nov 15, 2019
97dff46
Capitalise jsdoc
dbkr Nov 15, 2019
6d0237e
This now returns DeviceTrustLevel too
dbkr Nov 15, 2019
f0ba1f2
c+p fail
dbkr Nov 15, 2019
fa2e669
More jsdoc updates
dbkr Nov 15, 2019
2ab033e
is now implemented
dbkr Nov 15, 2019
5224ef4
This is now implemented
dbkr Nov 15, 2019
c550f83
update jsdoc
dbkr Nov 15, 2019
04b57bb
Remove ghost of some old code
dbkr Nov 15, 2019
5626126
Rename backup_password & functions
dbkr Nov 15, 2019
2a63cc4
Update import
dbkr Nov 15, 2019
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
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"presets": ["es2015"],
"presets": ["es2015", "es2016"],
jryans marked this conversation as resolved.
Show resolved Hide resolved
"plugins": [
"transform-class-properties",

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.18.0",
"babel-preset-es2016": "^6.24.1",
"browserify": "^16.2.3",
"browserify-shim": "^3.8.13",
"eslint": "^5.12.0",
Expand Down
141 changes: 141 additions & 0 deletions spec/test-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,144 @@ module.exports.awaitDecryption = function(event) {
});
});
};


const HttpResponse = module.exports.HttpResponse = function(
httpLookups, acceptKeepalives, ignoreUnhandledSync,
) {
this.httpLookups = httpLookups;
this.acceptKeepalives = acceptKeepalives === undefined ? true : acceptKeepalives;
this.ignoreUnhandledSync = ignoreUnhandledSync;
this.pendingLookup = null;
};

HttpResponse.prototype.request = function HttpResponse(
cb, method, path, qp, data, prefix,
) {
if (path === HttpResponse.KEEP_ALIVE_PATH && this.acceptKeepalives) {
return Promise.resolve();
}
const next = this.httpLookups.shift();
const logLine = (
"MatrixClient[UT] RECV " + method + " " + path + " " +
jryans marked this conversation as resolved.
Show resolved Hide resolved
"EXPECT " + (next ? next.method : next) + " " + (next ? next.path : next)
);
logger.log(logLine);

if (!next) { // no more things to return
if (method === "GET" && path === "/sync" && this.ignoreUnhandledSync) {
logger.log("MatrixClient[UT] Ignoring.");
return Promise.defer().promise;
}
if (this.pendingLookup) {
if (this.pendingLookup.method === method
&& this.pendingLookup.path === path) {
return this.pendingLookup.promise;
}
// >1 pending thing, and they are different, whine.
expect(false).toBe(
true, ">1 pending request. You should probably handle them. " +
"PENDING: " + JSON.stringify(this.pendingLookup) + " JUST GOT: " +
method + " " + path,
);
}
this.pendingLookup = {
promise: Promise.defer().promise,
method: method,
path: path,
};
return this.pendingLookup.promise;
}
if (next.path === path && next.method === method) {
logger.log(
"MatrixClient[UT] Matched. Returning " +
(next.error ? "BAD" : "GOOD") + " response",
);
if (next.expectBody) {
expect(next.expectBody).toEqual(data);
}
if (next.expectQueryParams) {
Object.keys(next.expectQueryParams).forEach(function(k) {
expect(qp[k]).toEqual(next.expectQueryParams[k]);
});
}

if (next.thenCall) {
process.nextTick(next.thenCall, 0); // next tick so we return first.
}

if (next.error) {
return Promise.reject({
errcode: next.error.errcode,
httpStatus: next.error.httpStatus,
name: next.error.errcode,
message: "Expected testing error",
data: next.error,
});
}
return Promise.resolve(next.data);
} else if (method === "GET" && path === "/sync" && this.ignoreUnhandledSync) {
logger.log("MatrixClient[UT] Ignoring.");
this.httpLookups.unshift(next);
return Promise.defer().promise;
}
expect(true).toBe(false, "Expected different request. " + logLine);
return Promise.defer().promise;
};

HttpResponse.KEEP_ALIVE_PATH = "/_matrix/client/versions";

HttpResponse.PUSH_RULES_RESPONSE = {
method: "GET",
path: "/pushrules/",
data: {},
};

HttpResponse.USER_ID = "@alice:bar";

HttpResponse.filterResponse = function(userId) {
const filterPath = "/user/" + encodeURIComponent(userId) + "/filter";
return {
method: "POST",
path: filterPath,
data: { filter_id: "f1lt3r" },
};
};

HttpResponse.SYNC_DATA = {
next_batch: "s_5_3",
presence: { events: [] },
rooms: {},
};

HttpResponse.SYNC_RESPONSE = {
method: "GET",
path: "/sync",
data: HttpResponse.SYNC_DATA,
};

HttpResponse.defaultResponses = function(userId) {
return [
HttpResponse.PUSH_RULES_RESPONSE,
HttpResponse.filterResponse(userId),
HttpResponse.SYNC_RESPONSE,
];
};

module.exports.setHttpResponses = function setHttpResponses(
client, responses, acceptKeepalives, ignoreUnhandledSyncs,
) {
const httpResponseObj = new HttpResponse(
responses, acceptKeepalives, ignoreUnhandledSyncs,
);

const httpReq = httpResponseObj.request.bind(httpResponseObj);
client._http = [
"authedRequest", "authedRequestWithPrefix", "getContentUri",
"request", "requestWithPrefix", "uploadContent",
].reduce((r, k) => {r[k] = expect.createSpy(); return r;}, {});
client._http.authedRequest.andCall(httpReq);
client._http.authedRequestWithPrefix.andCall(httpReq);
client._http.requestWithPrefix.andCall(httpReq);
client._http.request.andCall(httpReq);
};
77 changes: 77 additions & 0 deletions spec/unit/crypto/backup.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import testUtils from '../../test-utils';
import OlmDevice from '../../../lib/crypto/OlmDevice';
import Crypto from '../../../lib/crypto';
import logger from '../../../src/logger';
import olmlib from '../../../lib/crypto/olmlib';

const Olm = global.Olm;

Expand Down Expand Up @@ -83,6 +84,16 @@ const BACKUP_INFO = {
},
};

const keys = {};

function getCrossSigningKey(type) {
return keys[type];
}

function saveCrossSigningKeys(k) {
Object.assign(keys, k);
}

function makeTestClient(sessionStore, cryptoStore) {
const scheduler = [
"getQueueForEvent", "queueEvent", "removeEventFromQueue",
Expand All @@ -108,6 +119,7 @@ function makeTestClient(sessionStore, cryptoStore) {
deviceId: "device",
sessionStore: sessionStore,
cryptoStore: cryptoStore,
cryptoCallbacks: { getCrossSigningKey, saveCrossSigningKeys },
});
}

Expand Down Expand Up @@ -296,6 +308,71 @@ describe("MegolmBackup", function() {
});
});

it('signs backups with the cross-signing master key', async function() {
const groupSession = new Olm.OutboundGroupSession();
groupSession.create();
const ibGroupSession = new Olm.InboundGroupSession();
ibGroupSession.create(groupSession.session_key());

const client = makeTestClient(sessionStore, cryptoStore);

megolmDecryption = new MegolmDecryption({
userId: '@user:id',
crypto: mockCrypto,
olmDevice: olmDevice,
baseApis: client,
roomId: ROOM_ID,
});

megolmDecryption.olmlib = mockOlmLib;

await client.initCrypto();
let privateKeys;
client.uploadDeviceSigningKeys = async function(e) {return;};
client.uploadKeySignatures = async function(e) {return;};
client.on("crossSigning.saveCrossSigningKeys", function(e) {
privateKeys = e;
});
client.on("crossSigning.getKey", function(e) {
e.done(privateKeys[e.type]);
});
await client.resetCrossSigningKeys();
let numCalls = 0;
await new Promise(async (resolve, reject) => {
client._http.authedRequest = function(
callback, method, path, queryParams, data, opts,
) {
++numCalls;
expect(numCalls).toBeLessThanOrEqualTo(1);
if (numCalls >= 2) {
// exit out of retry loop if there's something wrong
reject(new Error("authedRequest called too many timmes"));
return Promise.resolve({});
}
expect(method).toBe("POST");
expect(path).toBe("/room_keys/version");
try {
// make sure auth_data is signed by the master key
olmlib.pkVerify(
data.auth_data, client.getCrossSigningId(), "@alice:bar",
);
} catch (e) {
reject(e);
return Promise.resolve({});
}
resolve();
return Promise.resolve({});
};
await client.createKeyBackupVersion({
algorithm: "m.megolm_backup.v1",
auth_data: {
public_key: "hSDwCYkwp1R0i33ctD73Wg2/Og0mOBr066SpjqqbTmo",
},
});
});
expect(numCalls).toBe(1);
});

it('retries when a backup fails', function() {
const groupSession = new Olm.OutboundGroupSession();
groupSession.create();
Expand Down
Loading