issueChallenge

In one atomic step: prunes every challenge that expired before LastDeviceRecoveryChallengeRequest.now; then

  • the target has no registration: LastDeviceRecoveryChallengeIssue.NotRegistered;

  • its user has no active recovery key (none, or revoked): LastDeviceRecoveryChallengeIssue.NotConfigured;

  • the target has an unexpired challenge issued for its current key and epoch and the current recovery key epoch: returns that one unchanged;

  • otherwise stores the request's candidate ID and nonce with the current key, epoch and recovery key epoch (replacing an outdated challenge of the target) and returns it.

So a target has at most one challenge, and requesting another one while it is valid returns the same challenge.