microg/GmsCore · error · RequestHandlingException

UNKNOWN_ERR

UNKNOWN_ERR

Error message

EID decrypt failed

What it means

The hybrid tunnel client holds the peer authenticator's EID (encrypted advertising identifier) plus a random seed. decryptEid() calls CryptoHelper.decryptEid(eid, randomSeed) and throws when it returns null, i.e. the EID could not be decrypted. Without the decrypted EID the client cannot build the BLE scan filter or derive the tunnel connection, so it fails with UNKNOWN_ERR.

Source

Thrown at play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/hybrid/transport/ClientTunnelTransport.kt:41

    private var decryptEid: ByteArray? = null

    fun startConnecting() {
        Log.d(TAG, "startConnecting: ")
        decryptEid = decryptEid()
        val routingId = decryptEid!!.sliceArray(11..13)
        val domainId = ((decryptEid!![15].toInt() and 0xFF) shl 8) or (decryptEid!![14].toInt() and 0xFF)
        val tunnelId = CryptoHelper.endif(ikm = randomSeed, salt = ByteArray(0), info = byteArrayOf(2, 0, 0, 0), length = 16)

        val webSocketConnectUrl = buildWebSocketConnectUrl(domainId, routingId, tunnelId)
        Log.d(TAG, "startConnecting: webSocketConnectUrl=$webSocketConnectUrl")
        if (websocket == null) {
            websocket = TunnelWebsocket(webSocketConnectUrl, this)
        }
        websocket?.connect()
    }

    private fun decryptEid(): ByteArray {
        val decryptEid = CryptoHelper.decryptEid(eid, randomSeed) ?: throw RequestHandlingException(ErrorCode.UNKNOWN_ERR, "EID decrypt failed")
        if (decryptEid.size != 16 || decryptEid[0] != 0.toByte()) {
            throw RequestHandlingException(ErrorCode.UNKNOWN_ERR, "EID structure invalid")
        }
        return decryptEid
    }

    fun stopConnecting() {
        Log.d(TAG, "stopConnecting: ")
        websocket?.close()
    }

    override fun disconnected() {
        Log.d(TAG, "disconnected: ")
        callback.onSocketClose()
    }

    override fun error(error: TunnelException) {
        Log.d(TAG, "error: ", error)

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Validate the eid and randomSeed payloads (length, encoding, base64 decodability) before starting the connection.
  2. Confirm both sides use the same hybrid/EID crypto version (update microG or the peer browser).
  3. Regenerate the handoff data — re-scan the QR code / restart the browser flow to get a fresh EID.
  4. Log eid.size and seed presence at the call site to distinguish null-input from bad-ciphertext cases.

Example fix

// before
val decryptEid = CryptoHelper.decryptEid(eid, randomSeed)
    ?: throw RequestHandlingException(ErrorCode.UNKNOWN_ERR, "EID decrypt failed")
// after
if (eid == null || randomSeed == null || eid.isEmpty || randomSeed.isEmpty) {
    throw RequestHandlingException(ErrorCode.UNKNOWN_ERR, "EID decrypt failed: missing eid or seed")
}
val decryptEid = CryptoHelper.decryptEid(eid, randomSeed)
    ?: throw RequestHandlingException(ErrorCode.UNKNOWN_ERR, "EID decrypt failed: ciphertext/key mismatch")
Defensive patterns

Strategy: validation

Validate before calling

require(!eid.isNullOrEmpty() && !randomSeed.isNullOrEmpty()) { "missing EID/seed for hybrid connection" }

Type guard

fun isValidHandoff(eid: ByteArray?, seed: ByteArray?) = eid != null && seed != null && eid.isNotEmpty && seed.isNotEmpty

Try / catch

try { transport.startConnecting() } catch (e: RequestHandlingException) { if (e.message == "EID decrypt failed") restartHandoffFlow() }

Prevention

When it happens

Trigger: decryptEid() (called from startConnecting and connected) is invoked with an eid/randomSeed pair that CryptoHelper.decryptEid cannot decrypt — malformed/empty eid bytes, wrong seed, or ciphertext from a peer with an incompatible key.

Common situations: Receiving a garbled or truncated EID from the QR code / browser handoff payload; version mismatch between the crypto used by the peer (e.g. Google Chrome or iOS device) and this library's CryptoHelper; copy-paste error when passing the seed.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06). Data as JSON: /api/errors/17b56bd79e99f753. Report an issue: GitHub.