Protected Recovery Phrases: Plan and Progress

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.


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.

    :bulb: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).

    :bulb: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.

    :bulb: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 :smiling_imp: (joking, don’t quote me on this))

  • To upgrade an existing phrase to “protected”, the user will need to input the recovery phrase.

    :bulb: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 (: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?)


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 (δ,ο,:scream:) 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).


Alright! Who’s actually going to implement this?

  • @lastmjs will implement the UI changes
  • @Oleksii will implement the canister changes

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!


I suggest keeping the “X” but changing the appearance to communicate that it’s disabled.

1 Like

As opposed to what? Only being able to remove the recovery phrase?

I think I saw some (pseudo?)code which added this as a boolean value and I would like to suggest that an enum be used instead to protect against boolean blindness.

I would hate for there to be a bug because someone made a programming error where they provided true or false but meant the opposite.

I propose something like this instead:

enum {

I’m not sure if this has any impact on the size concerns.

I’d love it if people could share links to PRs in this thread.

1 Like

Maybe the owner can use their names as answers to security questions :laughing: I personally prefer some passwords and a few more security layers added to it.

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.

One very likely “device” to be stolen is IMHO a non-physical device, and that would in our case be the “recovery phrase”. Is there anything we can do to prevent this theft?

Maybe we can require entering multiple authentication and/or recovery devices in order to remove the “recovery phrase”? The actual required mix of the devices would be open for discussion, of course.

Threshol sig the recovery phrase. The point is to divide the recover phrase into n tokens out of which any m tokens are required to recreate the recovery phrase. (m < n). See here ( GitHub - icdev2dev/bachao: Social Recovery of Internet Identity) for the concept & MVP.

I like that, I guess we’ll let @lastmjs experiment a bit and he can share some thoughts with us

Yes, but I realize now it would be a bit confusing

Agreed; I’m sure @Oleksii will come up with a nice solution, if there’s anything unclear we can discuss it on the PR once it’s submitted!

Agreed, there are many possible solutions we could implement; I suggest you contribute the idea in e.g. the original thread, and that we keep this thread for discussing the particular solution of “protected” recovery phrases!

1 Like

Alright, it’s been a week!

Quick update from our side:

  • As mentioned here, II now officially accepts external contributions.
  • We’ve gotten started on the “downgrade” test framework, which will be implemented in Rust (as opposed to the existing canister tests that are written in Haskell).

Note that we’ve also pushed some changes to the FAQ to clarify a few things regarding recovery phrases (as they are now), let me know if you have any feedback!

1 Like