SecureMessageServer

class SecureMessageServer<C : Any>(storage: ServerStorage, clock: Clock, deviceRegistrationAuthorizer: DeviceRegistrationAuthorizer<C>, recoveryKeyResetPolicy: RecoveryKeyResetPolicy? = null)

Blind relay: stores public prekeys and queues opaque envelopes. Never decrypts payloads and never holds client secrets.

Device-scoped operations need the device's authentication (docs/server-authentication.md): a device registers its authentication key once (registerDevice), which the host application must allow through deviceRegistrationAuthorizer; publishing prekeys, relaying an envelope and draining the mailbox then take an AuthenticatedDevice from authenticate. Only fetching a prekey bundle stays open to anyone. clock is the server time that request timestamps are checked against and that is recorded as the installation time of registered device authentication keys.

deviceRegistrationAuthorizer is required and has no allow-all default: KSecureMessage does not know who owns a user ID, so the host decides which device may become one of a user's devices (S1, docs/security-review-remediation.md). C is the host's request authentication context the authorizer decides with (S1.1, finding N1); registerDevice takes it per request.

recoveryKeyResetPolicy enables delayed recovery key resets (docs/recovery-key-reset.md) with the host's delay; null (the default) disables them: requests are refused, while an already pending reset can still be read, cancelled and completed.

Constructors

Link copied to clipboard
constructor(storage: ServerStorage, clock: Clock, deviceRegistrationAuthorizer: DeviceRegistrationAuthorizer<C>, recoveryKeyResetPolicy: RecoveryKeyResetPolicy? = null)

Functions

Link copied to clipboard
Link copied to clipboard

Cancels the pending reset resetId by the authenticated device of the user. The recovery key, its epoch and the challenges stay. Throws RecoveryKeyResetException.NotPending if no such reset is pending.

Cancels the pending reset named by authorization with the signature of the recovery key it would replace (no device needed). Throws RecoveryKeyResetException and changes nothing if any check fails.

Link copied to clipboard

Completes the pending recovery key reset with the statement's new key, by the authenticated device of the user, from the eligibility time on, proven by the new key. Throws RecoveryKeyResetException and changes nothing if any check fails. From the moment this returns RecoveryKeyResetCompletionOutcome.COMPLETED, only the new key authorizes last-device recoveries (at epoch + 1), and every outstanding challenge of the user is gone. A retry of the same completion returns RecoveryKeyResetCompletionOutcome.ALREADY_APPLIED.

Link copied to clipboard
Link copied to clipboard

The challenge for recovering target with its user's last-device recovery key: the target's outstanding challenge while it is valid for the current registration, else a new random one valid for dev.kreienbuehl.ksecuremessage.protocol.LastDeviceRecovery.CHALLENGE_LIFETIME. Public. Throws LastDeviceRecoveryException.TargetNotRegistered or LastDeviceRecoveryException.NotConfigured.

Link copied to clipboard

The pending recovery key reset of the authenticated device's user, visible to every registered device of the user.

The pending recovery key reset of query's user, for the holder of the current recovery key (no device needed): the query must be signed by the registered recovery key within the validity window. Throws RecoveryKeyResetException otherwise.

Link copied to clipboard

The recovery key state of the authenticated device's user (docs/recovery-key-lifecycle.md): unconfigured, active (epoch, installation time, public key) or revoked (epoch, revocation time).

Link copied to clipboard
suspend fun publishPreKeys(device: AuthenticatedDevice, publication: PreKeyPublication)

Publishes prekeys for the authenticated device. See PreKeyService.publish. Throws PreKeyPublicationException on rejection.

Link copied to clipboard

Removes and returns the authenticated device's envelopes, each sender's in relay order.

Link copied to clipboard

Replaces the registered device authentication key of authorization's target with its replacement key, authorized by another registered device of the same user and proven by the replacement key (docs/device-recovery.md). Throws DeviceRecoveryException and changes nothing if any check fails.

Link copied to clipboard

Replaces address's registered device authentication key with the statement's replacement key, authorized by the user's offline recovery key over the server-issued challenge and proven by the replacement key (docs/last-device-recovery.md). Consumes the challenge. Throws LastDeviceRecoveryException and changes nothing if any check fails.

Link copied to clipboard
suspend fun registerDevice(context: C?, registration: DeviceRegistration, body: ByteArray, authentication: RequestAuthentication?): Boolean

Registers the device authentication key of registration's address. context is the host's authentication context of this request (for example the authenticated account principal), null if the host could not establish one. body is the exact HTTP body the request carried and authentication must be signed with the key being registered. Returns true for a first registration, false if exactly this key was registered before.

Link copied to clipboard

Registers registration's public key as the last-device recovery key of the authenticated device's user (docs/last-device-recovery.md). Returns true if it was stored (the user had none, or its key was revoked: the recovery key epoch continues, docs/recovery-key-lifecycle.md), false if exactly this key is active. Throws LastDeviceRecoveryException.InvalidKeyRegistration if the registration is for another user or its proof of possession does not verify, LastDeviceRecoveryKeyException.Conflict if the user has another active recovery key (only rotateLastDeviceRecoveryKey replaces it), and LastDeviceRecoveryKeyException.EpochExhausted if the recovery key epoch cannot grow any more.

Link copied to clipboard

The authenticated device's registration metadata: its current authentication epoch, which a routine rotation statement has to name, and the server time its registered key was installed at. Metadata only: whether a rotation is due is the application's policy (docs/device-authentication-rotation.md), never the server's.

Link copied to clipboard
suspend fun relay(device: AuthenticatedDevice, envelope: EncryptedEnvelope)

Queues envelope for its recipient. When this returns, the envelope is ordered after every envelope of the same sender for the same recipient that was relayed before (MailboxRepository, docs/transport-ordering.md).

Link copied to clipboard

Requests a delayed reset of the authenticated device's user's offline recovery key, for when that key is lost (docs/recovery-key-reset.md). The reset is bound to the active key and epoch; its request time is clock's time and its eligibility time follows from the policy; the request chooses neither. If a reset is pending already, it is returned unchanged (created = false): the delay never restarts. Throws RecoveryKeyResetException.NotAvailable without a policy, and RecoveryKeyResetException for no active key, a changed registration or an exhausted epoch. Last-device recovery challenges stay valid.

Link copied to clipboard

Revokes the authenticated device's user's offline recovery key (docs/recovery-key-lifecycle.md), authorized by the device together with the current recovery key's signature. Throws RecoveryKeyLifecycleException and changes nothing if any check fails. Afterwards no last-device recovery is possible until a new key is registered, and every outstanding challenge and any pending recovery key reset of the user are gone.

Link copied to clipboard

Replaces address's registered device authentication key K1 with the statement's replacement key K2 in a routine rotation (docs/device-authentication-rotation.md): authorized by K1 (the registered key at the statement's epoch) and proven by K2. The statement is the request's authentication; no other device takes part. Throws DeviceAuthenticationRotationException and changes nothing if any check fails.

Link copied to clipboard

Replaces the authenticated device's user's offline recovery key with the statement's new key (docs/recovery-key-lifecycle.md): authorized by the device (ServerAuth, ProtectedEndpoint.ROTATE_LAST_DEVICE_RECOVERY_KEY) together with the current recovery key's signature, proven by the new key. Throws RecoveryKeyLifecycleException and changes nothing if any check fails. From the moment this returns RecoveryKeyRotationOutcome.ROTATED, only the new key authorizes last-device recoveries, and every outstanding challenge and any pending recovery key reset of the user are gone. Device registrations, prekeys and mailboxes are not touched.