LastDeviceRecoveryRepository

Last-device recovery state (docs/last-device-recovery.md, docs/recovery-key-lifecycle.md): per user the state of its offline recovery key (public key, recovery key epoch, transition metadata), and per device at most one outstanding server-issued challenge. Shares its atomicity with DeviceRegistrationRepository and AuthenticationNonceRepository: DeviceRegistrationRepository.replaceForLastDeviceRecovery consumes a challenge in the same step as it replaces the key, and a recovery key rotation or revocation claims its nonce, compares the authorizing device's registration and removes every challenge of the user in the same step as it changes the recovery key. Never holds private key material.

The recovery key epoch is 1 for a first registration and grows by one with every rotation, revocation, completed reset and registration after a revocation. It never decreases, never wraps and is never reset: at Long.MAX_VALUE no further transition is possible.

A user has at most one pending delayed recovery key reset (docs/recovery-key-reset.md). It is bound to the ACTIVE key and epoch it was requested for; every recovery key transition (registration, rotation, revocation, reset completion) removes it in the same atomic step, so a pending reset never outlives the state it was requested for.

Functions

Link copied to clipboard

Cancels the pending reset in one atomic step:

Link copied to clipboard

The stored challenge of target, expired or not, or null.

Link copied to clipboard

Completes the pending reset in one atomic step. In this order:

Link copied to clipboard

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

Link copied to clipboard

The pending recovery key reset of userId, or null. Read in one atomic step together with the recovery key state: throws IllegalStateException (fail closed) if a stored reset is not bound to the user's current ACTIVE key and epoch, which the transitions never allow.

Link copied to clipboard
abstract suspend fun recoveryKey(userId: UserId): ByteArray?

A copy of the active recovery public key of userId, or null (none registered, or revoked).

Link copied to clipboard
abstract suspend fun recoveryKeyState(userId: UserId): RecoveryKeyState?

The recovery key state of userId, or null if the user never registered one.

Link copied to clipboard
abstract suspend fun registerRecoveryKey(userId: UserId, publicKey: ByteArray, registeredAt: Instant): Boolean

Registers publicKey (32 bytes) as userId's recovery key:

Link copied to clipboard

Requests a delayed reset of the requester's user's recovery key in one atomic step. In this order:

Link copied to clipboard

Revokes the user's active recovery key in one atomic step. In this order:

Link copied to clipboard

Replaces the user's active recovery key in one atomic step. In this order: