Hi all!
There have been talks of making recovery phrases more secure. This is the plan to make it happen, and progress updates will follow here!
Relevant threads:
TL;DR: @lastmjs and @Oleksii will contribute a cool feature
On May 12th, @lastmjs, @Oleksii, @Litzi, @dostro, @frederikrothenberger and myself discussed how to make this happen. For the record, Identity Labs (@dostro, @Litzi & @Oleksii) and @lastmjs offered to actually implement the changes, so big thanks to them! We’re updating the contributing guidelines and getting some internal (DFINITY) approval; once that’s done, this contribution will hopefully open the door to more people sending patches for Internet Identity!
Problem recap
You created an Internet Identity anchor and used it to log in to the NNS dapp, where you staked 10,000 ICPs in neurons for 8 years. A few months later, an attacker steals one of the devices you use to authenticate.
- Current situation: The attacker deletes all your recovery and authentication devices. You lost access to your anchor. You despair. You know that, in a little under 8 years from now, someone will be able to cash in on our dissolved neuron and convert all that ICP (10,000 + reward) to cycles for a ChatRoulette clone on the IC. You become a pessimist. You find solace in crypto market crashes and red ICP price plots.
- With proposed solution: The attacker cannot delete your recovery phrase because it is protected, meaning you have to enter it in order to delete it. You recover your anchor, delete all (compromised) authentication devices and you add new fresh devices. You have learned a valuable lesson about securing your devices and start a thriving IT security business. You have many beautiful children.
Solution
As hinted in the paragraph above, the proposed solution is the allow users to make their recovery phrases “protected”, meaning that in order to delete a recovery phrase, you need to first type it.
Ok, now comes the boring part where we describe the UX of “protected recovery phrases” (codename: “protected recovery phrases”) in excruciating detail. See below for even more boring implementation detail.
Functional Spec (or “how will it actually work”)
Let’s go:
- From now on, there will be an option to make a phrase “protected”. By making a recovery phrase protected, you will not be able to delete it without being able to type it in.
- This only applies to recovery phrases; other devices (regular authentication devices, recovery FIDO devices) are not affected.
We focus on recovery phrases for 2 reasons: keep the change as simple as possible, and make sure we don’t limit ourselves for future enhancements to FIDO device security.
- This mechanism will be completely opt-in (optional and disabled by default).
The “protected” feature is optional because some (3rd party) canisters rely on users being able to delete recovery phrases without having access to the recovery phrase.
- Existing recovery phrases will not be upgraded to “protected” automatically, but they can be upgraded to “protected” by the user. New recovery phrases will not be protected by default.
We cannot automatically update existing phrases because current users may have forgotten their recovery phrases (can’t blame them, I surely did) and may wish to delete them and create new ones. We also cannot make new phrases “protected” by default because we want to avoid confusion (though we may do this in the future just to mess with them (joking, don’t quote me on this))
- To upgrade an existing phrase to “protected”, the user will need to input the recovery phrase.
Because users like me (who have forgotten their recovery phrase) get excited about new features and click “YES I DO” and most likely would be stuck forever with an unwanted “recovery phrase”. Happened once, still have the ring. (just kidding) (we also do this to make sure users know exactly what they’re getting into)
The UI won’t change much, but notice the lock () icon near the recovery phrase:
When the lock is closed, the phrase is protected. When the lock is open, the phrase is not protected. Clicking an open lock will prompt for the recovery phrase, and (if correct) will then upgrade the recovery phrase to protected. Clicking a closed lock will prompt for the recovery phrase and (if correct) downgrade the phrase to not protected. (@lastmjs thinking of this now: should we allow downgrading? also, should we simply not show the “X” for deleting when the phrase is locked?)
Implementation
Ok, if anyone’s still reading, let’s talk implementation.
A new canister method will be added for “updating” a recovery phrase. This will perform an atomic update, be it either upgrade to or downgrade from protected. This method will either return “success” or an error; the error can be either “bad recovery phrase” (if the provided phrase did not match what the canister has) or “no more space” which may happen because making a phrase protected takes a little more space (see next point).
(is anyone really still reading?)
Special care has to be taken in the canister since adding a bit of information to a phrase (protected: true/false) will grow the anchor record. Since each anchor is serialized/deserialized as candid individually, and since candid supports optional fields, the first approach will be to add an optional field (“protected”) to recovery phrases. This means that deserializing existing, non-protected phrases will Just Work™. If this doesn’t work, a new variant (δ,ο,) may be added to KeyType
; however we try to avoid this as this isn’t what KeyType
is for, and it may be used in the future (if we enable attestation).
Plan
Alright! Who’s actually going to implement this?
Thanks to them! In the meantime, DFINITY (ok, @nmattia) will work on the following:
- Make sure we can accept external contributions in time
- Provide a framework for downgrade tests (in case we need to rollback, esp. with the stable memory changes)
- Add haskell integration tests for the features (we’re not going to force anyone to learn haskell to contribute)
I’ll share more details on the timeline Soon™, but this should land by the end of May. Fingers crossed!