Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

1. Abstract

This spec proposes replacing the current Recovery Agent update mechanism (initiateRecoveryAgentUpdate, cancelRecoveryAgentUpdate, executeRecoveryAgentUpdate), that always requires two transactions, with a new flow (updateRecoveryAgent, revertRecoveryAgentUpdate) that only requires a second transaction when the update needs to be reverted. The proposal preserves the security delay through a revert window.

2. Motivation

In the current contract implementation the user has to first call initiateRecoveryAgentUpdate. This initiates a configurable (e.g. 14 days) period during which the update operation can be cancelled (with a cancelRecoveryAgentUpdate call). After the period is over anyone can call a permissionless executeRecoveryAgentUpdate transaction to finalize the update.

This introduces the following issues:

  1. The updater has to remember to execute the call after the delay, or someone needs to develop and maintain an auto-executing service.
  2. The new Recovery Agent isn’t set on-chain until the delay elapses, which can be problematic e.g. if the user wants to register a Recovery Agent after registering their World ID. The Recovery Agent cannot be sure this has been set up properly for a World ID.

3. Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

3.1 Method Changes

The existing 3 methods are replaced with 2 new methods:

CurrentProposed
initiateRecoveryAgentUpdateupdateRecoveryAgent
cancelRecoveryAgentUpdaterevertRecoveryAgentUpdate
executeRecoveryAgentUpdateremoved

3.2 Mechanism

Instead of delaying the Recovery Agent update on-chain, the updateRecoveryAgent function updates the address immediately, records the previous Recovery Agent in a mapping, and sets a delay on the account. During this delay period an account MUST NOT yet be recoverable with the new Recovery Agent.

  • The previous Recovery Agent remains valid until invalidAfter elapses and the user MAY revert the update during this window, restoring the previous Recovery Agent. This is to maintain the security mechanism when updating a Recovery Agent to mitigate takeover attacks.
  • The new Recovery Agent SHALL NOT be used until after invalidAfter. Once invalidAfter elapses without a revert, the previous Recovery Agent becomes invalid and the new Recovery Agent becomes the sole valid Recovery Agent.

3.3 Storage

This change requires storing the previousRecoveryAgent address and an invalidAfter timestamp per leafIndex in the WorldIDRegistry. The data stored is equivalent to what is currently stored in the contract but with inverted semantics:

Current:

struct PendingRecoveryAgentUpdate {
    address newRecoveryAgent;
    uint256 executeAfter;
}

mapping(uint256 => PendingRecoveryAgentUpdate) internal _pendingRecoveryAgentUpdates;

Proposed:

struct PreviousRecoveryAgentUpdate {
    address prevRecoveryAgent;
    uint256 invalidAfter;
}

mapping(uint256 => PreviousRecoveryAgentUpdate) internal _prevRecoveryAgentUpdates;

3.4 updateRecoveryAgent

updateRecoveryAgent uses the same authorization as initiateRecoveryAgentUpdate.

function updateRecoveryAgent(
    uint64 leafIndex,
    address newRecoveryAgent,
    bytes memory signature, // authenticator signature
    uint256 nonce
) external virtual onlyProxy onlyInitialized {

updateRecoveryAgent SHALL NOT be called again while the previous Recovery Agent is still valid (i.e. invalidAfter has not elapsed) and the call MUST revert if it does. While allowing a second call to overwrite the pending update (keeping previousRecoveryAgent and resetting invalidAfter) would be well-defined, it adds contract complexity. The same result is achieved more explicitly by reverting the current update first.

3.5 revertRecoveryAgentUpdate

revertRecoveryAgentUpdate can be executed by any Admin Authenticator (as defined in WIP-104) up until invalidAfter, matching the authorization model of the current cancelRecoveryAgentUpdate. Reverting a Recovery Agent update is a management operation, so a Proving Authenticator MUST NOT be able to authorize it.

function revertRecoveryAgentUpdate(
    uint64 leafIndex,
    bytes memory signature, // authenticator signature
    uint256 nonce
) external virtual onlyProxy onlyInitialized {

3.6 Interaction with recoverAccount

When recoverAccount is called, any active Recovery Agent update (i.e. invalidAfter has not yet elapsed) MUST be cleared automatically. This handles the attack scenario where an attacker compromises an authenticator, removes the user’s authenticators to lock them out, and initiates a Recovery Agent update. When the user recovers their account via the Recovery Agent, the malicious update is reverted as part of the recovery.

4. Rationale

This solution preserves the original security properties while enabling better ergonomics and user experience. Notably:

  1. No follow-up transaction required. The update takes effect immediately, removing the need for an execution step after the delay period.
  2. Equivalent security. The revert window ensures that an attacker who compromises an authenticator cannot immediately replace the Recovery Agent. Any Admin Authenticator retains the ability to revert the change during the delay period.

5. Security

  1. The delay period MUST be preserved to maintain the security guarantee that a compromised authenticator cannot unilaterally replace the Recovery Agent.
  2. During the delay period, both the previous and new Recovery Agents exist, but recovery MUST only be possible with the previous Recovery Agent until invalidAfter elapses.
  3. Any Admin Authenticator MAY revert the update at any point before invalidAfter, restoring the original state.

6. Backwards Compatibility

This WIP replaces the existing 3-step Recovery Agent update mechanism. Contracts implementing this spec MUST remove the initiateRecoveryAgentUpdate, cancelRecoveryAgentUpdate, and executeRecoveryAgentUpdate methods in favor of the new interface. Any pending Recovery Agent updates at the time of upgrade SHOULD be migrated or invalidated.