microg/GmsCore · error · SecurityException

Signature invalid

Error message

Signature invalid

What it means

The SignedResponse.unpack() extension verifies the server's signature over the DroidGuard bytecode payload with SignatureVerifier.verifySignature before protobuf-decoding it. If verification fails, a SecurityException('Signature invalid') is thrown rather than trusting and executing unverified byteCode. This protects the device from running tampered or man-in-the-middled DroidGuard VM bytecode.

Source

Thrown at play-services-droidguard/core/src/main/kotlin/org/microg/gms/droidguard/core/NetworkHandleProxyFactory.kt:52

    }

    fun createPingHandle(packageName: String, flow: String, callback: GuardCallback, pingData: PingData?): HandleProxy {
        if (!DroidGuardPreferences.isLocalAvailable(context)) throw IllegalAccessException("DroidGuard should not be available locally")
        val (vmKey, byteCode, bytes) = fetchFromServer(flow, createRequest(flow, packageName, pingData))
        return createHandleProxy(flow, vmKey, byteCode, bytes, callback, DroidGuardResultsRequest().also { it.clientVersion = 0 })
    }

    fun createLowLatencyHandle(flow: String?, callback: GuardCallback, request: DroidGuardResultsRequest?): HandleProxy {
        if (!DroidGuardPreferences.isLocalAvailable(context)) throw IllegalAccessException("DroidGuard should not be available locally")
        val (vmKey, byteCode, bytes) = readFromDatabase("fast") ?: throw Exception("low latency (fast) flow not available")
        return createHandleProxy(flow, vmKey, byteCode, bytes, callback, request)
    }

    fun SignedResponse.unpack(): Response {
        if (SignatureVerifier.verifySignature(data_!!.toByteArray(), signature!!.toByteArray())) {
            return Response.ADAPTER.decode(data_!!)
        } else {
            throw SecurityException("Signature invalid")
        }
    }

    private fun readFromDatabase(flow: String?): Triple<String, ByteArray, ByteArray>? {
        ProfileManager.ensureInitialized(context)
        val id = "$flow/${version.versionString}/${Build.FINGERPRINT}"
        return dgDb.get(id)
    }

    fun createRequest(flow: String?, packageName: String, pingData: PingData? = null, extra: ByteArray? = null): Request {
        ProfileManager.ensureInitialized(context)
        return Request(
                usage = Usage(flow, packageName),
                info = listOf(
                        KeyValuePair("BOARD", Build.BOARD),
                        KeyValuePair("BOOTLOADER", Build.BOOTLOADER),
                        KeyValuePair("BRAND", Build.BRAND),
                        KeyValuePair("CPU_ABI", Build.CPU_ABI),

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Remove any HTTP proxy/VPN or custom DroidGuard network server URL so responses come from the official endpoint unmodified
  2. Update microG to the latest version so SignatureVerifier trusts the current server signing key
  3. Clear the DroidGuard cache/database (DgDatabaseHelper data) and retry to discard possibly corrupted cached bytecode
  4. Retry the fetch on a different trusted network to rule out transient corruption or captive-portal rewriting

Example fix

// before
val response = signed.unpack() // may throw SecurityException

// after
val response = try {
    signed.unpack()
} catch (e: SecurityException) {
    Log.w(TAG, "DroidGuard response signature invalid; refetching", e)
    refetchSignedResponse().unpack()
}
Defensive patterns

Strategy: try-catch

Validate before calling

val verified = SignatureVerifier.verifySignature(
    signed.data_!!.toByteArray(), signed.signature!!.toByteArray())
if (!verified) refetch()

Try / catch

try {
    val response = signed.unpack()
} catch (e: SecurityException) {
    Log.w(TAG, "DroidGuard bytecode signature invalid", e)
    refetchFromTrustedServer()
}

Prevention

When it happens

Trigger: fetchFromServer() downloaded a SignedResponse whose signature does not verify against the expected DroidGuard signing key: tampered/proxied response, corrupted download, wrong server URL serving untrusted bytecode, or outdated signature verification keys in the client.

Common situations: Traffic routed through a MITM proxy/VPN that alters responses; pointing microG's DroidGuard network URL at a non-official server; stale microG version whose SignatureVerifier keys no longer match server-side signing keys; network corruption on flaky mobile connections.

Related errors


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