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

e2e key backups #684

Merged
merged 47 commits into from
Nov 21, 2018
Merged

e2e key backups #684

merged 47 commits into from
Nov 21, 2018

Conversation

uhoreg
Copy link
Member

@uhoreg uhoreg commented Aug 8, 2018

Follow-on work to #595

src/client.js Outdated
keys.push(key);
return this.importRoomKeys(keys);
} else {
callback("aargh!");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might need to do better than aargh ;)

@uhoreg
Copy link
Member Author

uhoreg commented Aug 23, 2018

@dbkr here is the initial draft of the API. I haven't really tested it to see if it actually works yet -- this is mainly to give a general idea of what the API might look like (and to give a somewhat plausible implementation). If you have any suggestions for improving the API (especially to make it fit in better with the rest of the SDK), let me know. As far as implementation goes, I'm guessing you'll probably want to just replace things with stubs when you're doing the UI work, until I get it all working and tested.

The general idea is that you would call client.getKeyBackupVersion to see if there is a usable backup. It'll throw an exception if there isn't. If there is, it will return an object, which you can pass to client.enableKeyBackup. Once client.enableKeyBackup is called, then any keys that are received will be backed up. Backups can be disabled by calling client.disableKeyBackup. A client can create a new backup version by calling client.createKeyBackupVersion. This will automatically enable the backup. client.createKeyBackupVersion will return a promise that will resolve to the backup recovery key, which can be presented to the user.

To restore from the key backup, you would call client.restoreKeyBackups with the backup recovery key, room ID (optional if session ID is not given), and session ID (optional). This should fetch the key(s) from the server and supposedly re-attempt to decrypt previous events.

You can also delete keys from the backup by calling client.deleteKeyBackups.

Currently missing are:

  • an API for backing up a key to the server (there is client.sendKeyBackup, but that's mainly intended for internal use, because it has the "data" parameter, which would be fiddly for people to attempt to build themselves)
  • a proper backup recovery key (need to figure out what it should look like)
  • probably other things that I'm forgetting right now

uhoreg and others added 19 commits August 23, 2018 00:29
Continues from uhoreg's branch
Add method to check if a given string is a valid recovery key
Remove commented code block as it's not immediately obvious it makes
sense or is the right way of suggesting a key restore.
wasm Olm has a new interface: it now has an init method that needs
to be called and the promise it returns waited on before the Olm
module is used. Support that, and allow Crypto etc to be imported
whether Olm is enabled or not. Change whether olm is enabled to
be async since now it will be unavailable if the async module init
fails. Don't call getOlmVersion() until the Olm.init() is done.
dbkr and others added 12 commits September 26, 2018 16:39
By doing `Olm = global.Olm` on script load, we require that Olm is
available right from the start, which isn't really necessary. As
long as it appears some time before we actually want to use it,
this is fine (we can probably assume it's not going to go away
again..?)

This means Riot doesn't need to faff about making sure olm is
loaded before starting anything else.
base58check seems way overcomplicated for this purpose (plus the
module was exporting an es6 file, breaking the js-sdk build). A
parity check empirically detects single substitution and transposition
errors. Another option would be Luhn's algorithm.
Copy link
Member

@dbkr dbkr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good. Remaining things I think:

sessionData: sessionGetReq.result.session
});
}
//sessions.push(cursor.value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should probably remove this

const objectStore = txn.objectStore("sessions_needing_backup");
return Promise.all(sessions.map((session) => {
return new Promise((resolve, reject) => {
console.log(session);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stray debug logging

}
);

this._maybeSendKeyBackup();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should await on this so the backupAllGroupSessions promise doesn't complete until the backup is done

sessionId: sessionId,
}]);

this._maybeSendKeyBackup();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably worth awaiting on this too? Actually possibly not: when we call it from megolm we only want to wait for it to be saved to indexeddb, not actually backed up. worth a comment though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think it does make sense to await here, and then remove the awaits from megolm.

// FIXME: pause
}
} while (!successful);
// FIXME: pause between iterations?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to consolidate my comments from the individual commits, we should put the pause in, or just stop & retry next time a backup is triggered which would be my inclination.

@dbkr dbkr mentioned this pull request Oct 12, 2018
@dbkr
Copy link
Member

dbkr commented Oct 16, 2018

lgtm once new Olm is released

@uhoreg uhoreg changed the title [WIP] e2e key backups e2e key backups Oct 16, 2018
Copy link
Member Author

@uhoreg uhoreg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argh! Why can't I "Request changes" on my own PR. :(

const encrypted = this.backupKey.encrypt(JSON.stringify(sessionData));

data[roomId]['sessions'][session.sessionId] = {
first_message_index: 1, // FIXME
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment to make sure I don't keep forgetting to fix this

return null;
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit magic, but given the name of the function I can't really suggest anything better. Maybe in the future we can deprecate the 3 arg usage of this function altogether.

},
);

await this._maybeSendKeyBackup();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backupAllGroupSessions is called on user action but _maybeSendKeyBackup can delay its actual execution randomly up to 10s.
In the context of this function, we would like _maybeSendKeyBackup to start ASAP.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NB. I've added a delay param in #736

delete sessionData.first_known_index;
const encrypted = this.backupKey.encrypt(JSON.stringify(sessionData));

const forwardedCount =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no forwardingCurve25519KeyChain field in the dict returned by exportInboundGroupSession but forwarding_curve25519_key_chain.

The mix of forwarding_curve25519_key_chain and forwardingCurve25519KeyChain in the js code makes me terrified.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spot - turns out this was completely broken

@@ -956,6 +973,18 @@ MegolmDecryption.prototype.importRoomKey = function(session) {
session.sender_claimed_keys,
true,
).then(() => {
if (this._crypto.backupInfo) {
// don't wait for it to complete
this._crypto.backupGroupSession(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this method is called because we are restoring keys from the current backup session, it looks inefficient to send back up the key to the homeserver.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, at some point we may want to flag keys that came from the backup in the first place (and what version)

@uhoreg
Copy link
Member Author

uhoreg commented Oct 30, 2018

replaced by #736

@dbkr dbkr merged commit 322ef1f into matrix-org:develop Nov 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants