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
- 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
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
- 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
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
- Network URL required
- Access denied, missing google package permission for
- DroidGuard should not be available locally
- IntegrityErrorCode.NETWORK_ERROR
- suggested UID [
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/55f5c1e2222494bd.
Report an issue: GitHub.