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
- Validate the eid and randomSeed payloads (length, encoding, base64 decodability) before starting the connection.
- Confirm both sides use the same hybrid/EID crypto version (update microG or the peer browser).
- Regenerate the handoff data — re-scan the QR code / restart the browser flow to get a fresh EID.
- 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
- Base64-decode handoff payloads defensively and log sizes before use
- Keep microG and the peer browser on compatible hybrid protocol versions
- Treat a failed decrypt as 'regenerate the QR/handoff' rather than retrying the same bytes
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
- Transport ${transport} not supported
- Attachment ${attachment} not supported
- Attestation conveyance preference ${attachment} not supporte
- No response set.
- PublicKeyCredentialType ${type} not supported
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/17b56bd79e99f753.
Report an issue: GitHub.