microg/GmsCore · error · StandardIntegrityException

IntegrityErrorCode.NONCE_TOO_SHORT

IntegrityErrorCode.NONCE_TOO_SHORT

Error message

Nonce too short.

What it means

The nonce byte array supplied to requestIntegrityToken is shorter than the 16-byte minimum enforced by the service. Nonces must be long enough to be unguessable; anything under 16 bytes is rejected with IntegrityErrorCode.NONCE_TOO_SHORT.

Source

Thrown at vending-app/src/main/kotlin/com/google/android/finsky/integrityservice/IntegrityService.kt:122

            runCatching {
                val packageName = request.getString(KEY_PACKAGE_NAME)
                if (packageName == null) {
                    throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, "Null packageName.")
                }
                integrityData = callerAppToIntegrityData(context, packageName)
                if (integrityData?.allowed != true) {
                    throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, "Not allowed to request integrity token.")
                }
                val playIntegrityEnabled = VendingPreferences.isDeviceAttestationEnabled(context)
                if (!playIntegrityEnabled) {
                    throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, "API is disabled.")
                }
                val nonceArr = request.getByteArray(KEY_NONCE)
                if (nonceArr == null) {
                    throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, "Nonce missing.")
                }
                if (nonceArr.size < 16) {
                    throw StandardIntegrityException(IntegrityErrorCode.NONCE_TOO_SHORT, "Nonce too short.")
                }
                if (nonceArr.size >= 500) {
                    throw StandardIntegrityException(IntegrityErrorCode.NONCE_TOO_LONG, "Nonce too long.")
                }
                val cloudProjectNumber = request.getLong(KEY_CLOUD_PROJECT, 0L)
                val playCoreVersion = request.getPlayCoreVersion()
                Log.d(TAG, "requestIntegrityToken(packageName: $packageName, nonce: ${nonceArr.encodeBase64(false)}, cloudProjectNumber: $cloudProjectNumber, playCoreVersion: $playCoreVersion)")

                val packageInfo = context.packageManager.getPackageInfoCompat(packageName, SIGNING_FLAGS)
                val timestamp = makeTimestamp(System.currentTimeMillis())
                val versionCode = packageInfo.versionCode

                val integrityParams = IntegrityParams(
                    packageName = PackageNameWrapper(packageName),
                    versionCode = VersionCodeWrapper(versionCode),
                    nonce = nonceArr.encodeBase64(noPadding = false, noWrap = true, urlSafe = true),
                    certificateSha256Digests = packageInfo.signaturesCompat.map {
                        it.toByteArray().sha256().encodeBase64(noPadding = true, noWrap = true, urlSafe = true)

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Generate the nonce with a cryptographic source of at least 16 bytes, e.g. ByteArray(16).also { SecureRandom().nextBytes(it) } or a full SHA-256 digest (32 bytes).
  2. Check nonceArr.size >= 16 at the call site before sending the request.
  3. If using a hash, do not truncate it — keep the full digest bytes.
  4. Ensure any Base64 decode of the nonce is correct so it yields the expected length.

Example fix

// before
val nonce = System.currentTimeMillis().toString().toByteArray() // ~13 bytes
// after
val nonce = ByteArray(32).also { SecureRandom().nextBytes(it) } // or MessageDigest SHA-256 of request data
Defensive patterns

Strategy: validation

Validate before calling

fun isNonceTooShort(nonce: ByteArray) = nonce.size < 16
if (isNonceTooShort(nonce)) throw IllegalArgumentException("Nonce must be >= 16 bytes")

Type guard

fun ByteArray?.isNonceValid(): Boolean = this != null && size in 16..499

Try / catch

try {
    integrityManager.requestIntegrityToken(request)
} catch (e: StandardIntegrityException) {
    if (e.errorCode == IntegrityErrorCode.NONCE_TOO_SHORT) {
        regenerateNonceAndRetry()
    } else throw e
}

Prevention

When it happens

Trigger: Calling requestIntegrityToken with a KEY_NONCE byte array whose size < 16, e.g. a short string converted to bytes or a truncated hash.

Common situations: Using short custom strings like a timestamp or user ID as the nonce; truncating a hash/digest to a few bytes; encoding issues that shrink the payload (e.g. wrong Base64 decode); generating a nonce from a 8-byte value.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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