{"record":{"id":"55f5c1e2222494bd","repo":"microg/GmsCore","slug":"signature-invalid","errorCode":null,"errorMessage":"Signature invalid","messagePattern":"Signature invalid","errorType":"exception","errorClass":"SecurityException","httpStatus":null,"severity":"error","filePath":"play-services-droidguard/core/src/main/kotlin/org/microg/gms/droidguard/core/NetworkHandleProxyFactory.kt","lineNumber":52,"sourceCode":"    }\n\n    fun createPingHandle(packageName: String, flow: String, callback: GuardCallback, pingData: PingData?): HandleProxy {\n        if (!DroidGuardPreferences.isLocalAvailable(context)) throw IllegalAccessException(\"DroidGuard should not be available locally\")\n        val (vmKey, byteCode, bytes) = fetchFromServer(flow, createRequest(flow, packageName, pingData))\n        return createHandleProxy(flow, vmKey, byteCode, bytes, callback, DroidGuardResultsRequest().also { it.clientVersion = 0 })\n    }\n\n    fun createLowLatencyHandle(flow: String?, callback: GuardCallback, request: DroidGuardResultsRequest?): HandleProxy {\n        if (!DroidGuardPreferences.isLocalAvailable(context)) throw IllegalAccessException(\"DroidGuard should not be available locally\")\n        val (vmKey, byteCode, bytes) = readFromDatabase(\"fast\") ?: throw Exception(\"low latency (fast) flow not available\")\n        return createHandleProxy(flow, vmKey, byteCode, bytes, callback, request)\n    }\n\n    fun SignedResponse.unpack(): Response {\n        if (SignatureVerifier.verifySignature(data_!!.toByteArray(), signature!!.toByteArray())) {\n            return Response.ADAPTER.decode(data_!!)\n        } else {\n            throw SecurityException(\"Signature invalid\")\n        }\n    }\n\n    private fun readFromDatabase(flow: String?): Triple<String, ByteArray, ByteArray>? {\n        ProfileManager.ensureInitialized(context)\n        val id = \"$flow/${version.versionString}/${Build.FINGERPRINT}\"\n        return dgDb.get(id)\n    }\n\n    fun createRequest(flow: String?, packageName: String, pingData: PingData? = null, extra: ByteArray? = null): Request {\n        ProfileManager.ensureInitialized(context)\n        return Request(\n                usage = Usage(flow, packageName),\n                info = listOf(\n                        KeyValuePair(\"BOARD\", Build.BOARD),\n                        KeyValuePair(\"BOOTLOADER\", Build.BOOTLOADER),\n                        KeyValuePair(\"BRAND\", Build.BRAND),\n                        KeyValuePair(\"CPU_ABI\", Build.CPU_ABI),","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-droidguard/core/src/main/kotlin/org/microg/gms/droidguard/core/NetworkHandleProxyFactory.kt#L34-L70","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Remove any HTTP proxy/VPN or custom DroidGuard network server URL so responses come from the official endpoint unmodified","Update microG to the latest version so SignatureVerifier trusts the current server signing key","Clear the DroidGuard cache/database (DgDatabaseHelper data) and retry to discard possibly corrupted cached bytecode","Retry the fetch on a different trusted network to rule out transient corruption or captive-portal rewriting"],"exampleFix":"// before\nval response = signed.unpack() // may throw SecurityException\n\n// after\nval response = try {\n    signed.unpack()\n} catch (e: SecurityException) {\n    Log.w(TAG, \"DroidGuard response signature invalid; refetching\", e)\n    refetchSignedResponse().unpack()\n}","handlingStrategy":"try-catch","validationCode":"val verified = SignatureVerifier.verifySignature(\n    signed.data_!!.toByteArray(), signed.signature!!.toByteArray())\nif (!verified) refetch()","typeGuard":null,"tryCatchPattern":"try {\n    val response = signed.unpack()\n} catch (e: SecurityException) {\n    Log.w(TAG, \"DroidGuard bytecode signature invalid\", e)\n    refetchFromTrustedServer()\n}","preventionTips":["Never point the DroidGuard network URL at untrusted servers","Avoid MITM proxies/interceptors on DroidGuard traffic","Keep microG updated so signature verification keys match the server","Clear cached bytecode after signature failures"],"tags":["android","microg","droidguard","security","signature","network"],"backgroundTag":"signature-verification-failed","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}