issueChallenge
abstract suspend fun issueChallenge(request: LastDeviceRecoveryChallengeRequest): LastDeviceRecoveryChallengeIssue
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.