-
-
Notifications
You must be signed in to change notification settings - Fork 112
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
Encrypted device sync #1373
Encrypted device sync #1373
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First of all, thank you for your work, this is AWESOME! #1304 is actually the most important wallet feature right now but we realized that we didn't set priority:high
on it. We corrected this now so you actually get 2x more sats for #1304.
Regarding the code: I've played around with it and looked through the code and I like how you interfaced it nicely with the existing wallet code (just as you mentioned), nice work! I also tested syncing of wallets using Brave and Firefox and it works well on the happy path.
However, I noticed that it's possible to get the devices out of sync by resetting one. The other device shows disconnected now but is not actually reset (key still exists on device):
2024-09-18.22-57-01.mp4
If you now have a wallet configured that will get synced across the devices, decryption errors are thrown on the device that wasn't reset and is now "connected" with a wrong passphrase:
2024-09-18.23-00-20.mp4
Another thing I noticed is that the UI/UX could be improved. Going through the setup, I think we can simplify it. I think it's currently a bit intimidating to use since it's in a separate tab which makes it stand out and it might involve more than one step to setup because of the possible prompt for migration which sounds scary. After setup, there are three buttons in different colors.
For example, do we need this "scary" migration prompt? Does it make sense to enable this setting but not migrate? And can we express the state without three separate buttons that can be overwhelming and confusing?
I think we might be able to express everything a user might want to do with a single button on the settings page in a familiar layout and colors. The flow could be similar to how API keys are implemented. I'll give it a shot.
I've left some other suggestions but they are mostly about minor things.
Thank you for the review.
This should be fixed now, i've added some code to clear the stored key if the local and remote hashes mismatch
The only situation i could think of, where this makes sense, is when you have different conflicting settings on different devices and you want to chose which one is migrated. I think another solution is making the migration not overwrite keys, so that the first device that connects gets the precedence. I've changed the code to do that and got rid of the popup. Also changed the ux to have a single button under general, what do you think? : There was also some code in |
Just a heads up: I'll be reviewing this tomorrow. I suspect it'll be merged then too. |
Someone reminded me that when this is enabled, we no longer need to delete the wallets on logout. This isn't included in this PR yet though. |
This reminds me that it is probably a good practice to delete the vault key on logout. My last two commits:
last point is to fix an error with the webln wallet that, when webln was not available, was trying to save its localOnly config to disable itself, and erroring because |
I'm impressed - code is clear and simple, and this problem isn't simple at all. Please lmk if you're looking for a job! I was only able to get to this at the end of my day. The one thing we might really want to change before merging it is storing the keys as non-extractable in indexeddb rather than in localstorage. See: denoland/deno#11481 (comment). (I mentioned this to ek last week, but neither of us had done enough research to know this could be done.) Basically, we'd generate the keys on device, store the Regardless, you've more than qualified for the full bounty and I'll send that to you the moment you send me a 2m sat invoice. If you'd like, ek or I can make the remaining changes. Up to you! I made myself notes while reviewing that I'm going to put here for convenience (don't feel like you need address these if you don't want to):
|
Thank you for the kind words! It is also thanks to @ekzyis improvements and suggestions.
I've read the post you linked, and i am pretty sure it can be implemented easily, but for our specific use case:
The only security improvement this would make, that i could think of is that, since we use bip39, an app (with enough permissions) could scan the filesystem for known words and should be able to find the passphrase in the format it is currently stored, while using the non extractable CryptoKey will provide some level of obfuscation, but:
My main concerns in switching the logic to indexeddb are:
That said, the end result shouldn't be too bad code wise imo, so we could still move forward with this. Thoughts?
No worries, I'll look into fixing at least some of them 🫡 |
could something like this work? the reset button appears only if device sync is configured but the user is disconnected
I am not sure how to make this look good in the current layout (maybe an extra top margin on the reset button?)
is something like this preferable: 98089bd ? |
The non-extractable key cannot be trivially serialized and sent off device afaik, so the attacker would have to decrypt the vault on the device and send it off. That seems a step or two more targeted. Beyond that, not having access to the key ourselves will prevent us from accidentally doing something that reveals it. The CryptoKey API was specifically designed for this kind of thing, so I suspect it's the best tool we have for the job.
Neither method is bulletproof, but I don't think they're the same. Most security stuff is a game of inches afaict. |
Yep, I think that works.
They can stay close in the current layout. We should probably create a reset prompt/barrier though if we don't already have one.
Cool! Lots of buttons, but I don't mind the "packaging" of v0 things being wip so long as the functionality is there.
Toast seems to be the pattern we are using these days. |
Makes sense, I hadn’t considered it that way. I moved the serialization/deserialization code into the set/get localkey functions, so the rest of the code needs to deal only with CryptoKey objects without worrying if it comes from the indexedDB or localStorage backends. |
I ended up changing two things:
|
I ended up reverting this because given the way wallets are loaded, every time It does this mostly because we don't know the wallet priority and need to grab all of them to see the wallet priority, and the To support device sync, we'll likely need a polling context that grabs the vault periodically so that we have a predictable number of API calls to support it. This shouldn't be too difficult but it's a bit more involved. I have some work locally making progress on this. |
This reverts commit 1534559.
Description
This PR introduces a general purpose approach to end to end device sync and implements it in wallet attachments to solve #1304 .
Screenshots
Additional Context
It is developed to be a drop-in replacement to
components/use-local-state.js
, usage is as follows:the useVaultStorageState hook handles everything internally and detects if the user has device sync enabled and fallbacks to localStorage if not, so there is no other code required to use this feature other than the one presented above.
useVaultStorageState needs to rely on localStorage to store the passphrase and the stored values if the user doesn't have device sync enabled. To prevent possible name conflicts it prefixs every value with "vault:" and suffixes with the userid.
Checklist
Are your changes backwards compatible? Please answer below:
yes it is backward compatible and everything that will be set using
user-vault-state
and everything that was set before using wallet attachments will automatically migrate to the encrypted device sync when the user enables itin details:
user-vault-state
is backward compatible: if a key is not available with the new vault:key:userId syntax, it will try to read it normally from the localStorageuser-vault-state
will be automatically migrated to device sync when the users enables it (and confirms the dialog).On a scale of 1-10 how well and how have you QA'd this change and any features it might affect? Please answer below:
8
For frontend changes: Tested on mobile? Please answer below:
yes
Did you introduce any new environment variables? If so, call them out explicitly here:
no