Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Add support for dehydrated devices #5239

Merged
merged 12 commits into from
Oct 5, 2020
Merged

Conversation

uhoreg
Copy link
Member

@uhoreg uhoreg commented Sep 18, 2020

@uhoreg uhoreg requested a review from a team September 19, 2020 01:27
@uhoreg uhoreg marked this pull request as ready for review September 19, 2020 01:27
Copy link
Collaborator

@jryans jryans left a comment

Choose a reason for hiding this comment

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

I think I am a little confused about how all the pieces fit together, but I'm sure we'll get there. 😅

One thing that I'd like advice on is, when the user enters a passphrase/key to rehydrate the device, it should automatically try to unlock SSSS using the same passphrase/key. But with the current patch, the user has to tell Element that they want to set up SSSS, then say that they want to use a passphrase, and then it will automatically unlock. I'm not sure how to make it not prompt the user, but instead to automatically try to unlock it.

I'm still a bit puzzled on how the dehydration key also functions as a 4S key, but anyway, I'll assume that's a reasonable things for the moment... You could call accessSecretStorage which would eventually rely on getSecretStorageKey to retrieve the 4S key, but I suppose you only want this to happen when dehydration exists and only to try that key silently... so perhaps in some reasonable spot just after login, if you know you have a dehydration key, you could call bootstrapCrossSigning and / or bootstrapSecretStorage directly, but that would still rely on the global getSecretStorageKey helper... maybe this works means it's time for bootstrapFoo functions to take getSecretStorageKey as a parameter, so you can pass a customised version just for the dehydration key case which doesn't use any dialogs?

src/Lifecycle.js Outdated Show resolved Hide resolved
src/Login.js Outdated Show resolved Hide resolved
src/MatrixClientPeg.ts Outdated Show resolved Hide resolved
src/MatrixClientPeg.ts Outdated Show resolved Hide resolved
src/SecurityManager.js Outdated Show resolved Hide resolved
@uhoreg
Copy link
Member Author

uhoreg commented Sep 30, 2020

Still have not managed to get it to not prompt to unlock SSSS if the dehydration key works, but would like a review of the current state anyways.

The test failure is probably because I'm still based on an old version of develop.

@uhoreg uhoreg requested a review from jryans September 30, 2020 05:27
src/Lifecycle.js Outdated Show resolved Hide resolved
// If we just logged in, try to rehydrate a device instead of using a
// new device. If it succeeds, we'll get a new device ID, so make sure
// we persist that ID to localStorage
const newDeviceId = await client.rehydrateDevice();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a way to know that device ID came from rehydration? That would allow us to know the same information as freshLogin without needing an additional flag to track it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry, I don't really understand this comment. The freshLogin flag tells us whether we just logged in or not. And if so, then it tries to rehydrate the device. So at the point where the freshLogin flag is set, we haven't tried to rehydrate yet. We only want to try to rehydrate if we just logged in so that we don't replace a device that is already in use with the dehydrated device.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess I am trying to ask if there is any other way to know whether a rehydration is needed, instead of adding a new flag freshLogin. It would be nice to skip this work if it is not needed, for example.

Copy link
Member Author

Choose a reason for hiding this comment

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

I see. There's two different issues here. The first is whether there's a dehydrated device ready to be rehydrated. If there isn't a dehydrated device, the rehydrateDevice function will basically only make one call to the server and then return, so it shouldn't be much work.

The second issue is whether we just logged in, which is what the freshLogin flag checks for. If, for example, someone reloads Element, we don't want it to try to dehydrate a device even if there is one available. We want it to keep using the device it was already using. So we only check for a dehydrated device if the user just logged in.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Okay, I think I am following. As an alternative to storing mx_fresh_login in local storage when it is fresh, could we instead rely on knowing that the _restoreFromLocalStorage means we must not be a fresh login, and so it could pass something freshLogin: false through to _doSetLoggedIn without needing extra storage...?

Sorry if this feels a bit pedantic... 😅 I'm trying to finding chances to reduce complexity (even if a tiny amount), since login is already very complex.

Copy link
Member Author

Choose a reason for hiding this comment

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

could we instead rely on knowing that the _restoreFromLocalStorage means we must not be a fresh login

Unfortunately, we can't do that. When we do an SSO login, it saves the state to storage, and then reloads the page (why? I have no idea), so that means that it will _restoreFromLocalStorage even though it's a fresh login.

if (dehydrationInfo.key) {
try {
const key = dehydrationInfo.key;
if (await MatrixClientPeg.get().checkSecretStorageKey(key, keyInfo)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

For this to work, doesn't it mean there would need to be a step that attempts creating 4S with the dehydration key as the 4S key? Is that future work, or does some change here cause that to happen and I just haven't spotted it yet...? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

For now, we're not going to create 4S using the dehydration key, but we will unlock 4S using the dehydration key if it matches.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Okay, but then... why do we want to do that, since we know that at the moment, it will always fail since 4S wasn't created with the dehydration key?

It seems like we would only want to start trying dehydration as a 4S key when there's some reason to believe it would work.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry, I missed this question earlier.

Even though we don't create 4S using the dehydration key, we do dehydrate the device using the 4S key, so they keys will be the same.

src/SecurityManager.js Show resolved Hide resolved
@uhoreg uhoreg requested a review from jryans October 2, 2020 04:16
@uhoreg
Copy link
Member Author

uhoreg commented Oct 2, 2020

Not sure why the test is failing. The failing tests seem unrelated to code that I've changed. I'll try to debug more tomorrow, but I think this is otherwise ready for re-review.

src/settings/Settings.ts Outdated Show resolved Hide resolved
// If we just logged in, try to rehydrate a device instead of using a
// new device. If it succeeds, we'll get a new device ID, so make sure
// we persist that ID to localStorage
const newDeviceId = await client.rehydrateDevice();
Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess I am trying to ask if there is any other way to know whether a rehydration is needed, instead of adding a new flag freshLogin. It would be nice to skip this work if it is not needed, for example.

Copy link
Collaborator

@jryans jryans left a comment

Choose a reason for hiding this comment

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

A few more changes I think, especially to ensure the feature flag guards the new code paths. It's especially important to be a bit more conservative for changes in the login path like this.

function cacheSecretStorageKey(keyId, key) {
export async function getDehydrationKey(keyInfo, checkFunc) {
const inputToKey = makeInputToKey(keyInfo);
const { finished } = Modal.createTrackedDialog("Access Secret Storage dialog", "",
Copy link
Collaborator

Choose a reason for hiding this comment

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

This will ask for the dehydration key using UI that talks about Secure Backup, which worries me that users will get lost in yet another key... If we are not yet taking steps to unify 4S and dehydration key (so the dehydration key may exist and be different), then it seems like we need fresh designs for the UI that asks for it, so as to avoid confusion.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I suppose since there's a feature flag now, we wouldn't need to block merging on those designs, but it feels like a concern to me that would block enabling it later.

src/SecurityManager.js Show resolved Hide resolved
src/SecurityManager.js Show resolved Hide resolved
@uhoreg uhoreg requested a review from jryans October 5, 2020 16:05
Copy link
Collaborator

@jryans jryans left a comment

Choose a reason for hiding this comment

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

Since we have a disabled feature flag, this at least seems okay to me from a risk perspective.

However, there are still two points that I am unsure of that would be good to at least get your comments on:

  • I'm curious if we can simplify the fresh logic detection with less storage and less conditionals, see my comment today on that thread
  • I still don't understand why we want to try the dehydration key as the 4S key (see thread), so more details on the motivation or why it makes sense would help

// If we just logged in, try to rehydrate a device instead of using a
// new device. If it succeeds, we'll get a new device ID, so make sure
// we persist that ID to localStorage
const newDeviceId = await client.rehydrateDevice();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Okay, I think I am following. As an alternative to storing mx_fresh_login in local storage when it is fresh, could we instead rely on knowing that the _restoreFromLocalStorage means we must not be a fresh login, and so it could pass something freshLogin: false through to _doSetLoggedIn without needing extra storage...?

Sorry if this feels a bit pedantic... 😅 I'm trying to finding chances to reduce complexity (even if a tiny amount), since login is already very complex.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants