{"record":{"id":"313ac3f4e90c6736","repo":"microg/GmsCore","slug":"integrityerrorcode-nonce-too-short","errorCode":"IntegrityErrorCode.NONCE_TOO_SHORT","errorMessage":"Nonce too short.","messagePattern":"Nonce too short\\.","errorType":"error_code","errorClass":"StandardIntegrityException","httpStatus":null,"severity":"error","filePath":"vending-app/src/main/kotlin/com/google/android/finsky/integrityservice/IntegrityService.kt","lineNumber":122,"sourceCode":"            runCatching {\n                val packageName = request.getString(KEY_PACKAGE_NAME)\n                if (packageName == null) {\n                    throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, \"Null packageName.\")\n                }\n                integrityData = callerAppToIntegrityData(context, packageName)\n                if (integrityData?.allowed != true) {\n                    throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, \"Not allowed to request integrity token.\")\n                }\n                val playIntegrityEnabled = VendingPreferences.isDeviceAttestationEnabled(context)\n                if (!playIntegrityEnabled) {\n                    throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, \"API is disabled.\")\n                }\n                val nonceArr = request.getByteArray(KEY_NONCE)\n                if (nonceArr == null) {\n                    throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, \"Nonce missing.\")\n                }\n                if (nonceArr.size < 16) {\n                    throw StandardIntegrityException(IntegrityErrorCode.NONCE_TOO_SHORT, \"Nonce too short.\")\n                }\n                if (nonceArr.size >= 500) {\n                    throw StandardIntegrityException(IntegrityErrorCode.NONCE_TOO_LONG, \"Nonce too long.\")\n                }\n                val cloudProjectNumber = request.getLong(KEY_CLOUD_PROJECT, 0L)\n                val playCoreVersion = request.getPlayCoreVersion()\n                Log.d(TAG, \"requestIntegrityToken(packageName: $packageName, nonce: ${nonceArr.encodeBase64(false)}, cloudProjectNumber: $cloudProjectNumber, playCoreVersion: $playCoreVersion)\")\n\n                val packageInfo = context.packageManager.getPackageInfoCompat(packageName, SIGNING_FLAGS)\n                val timestamp = makeTimestamp(System.currentTimeMillis())\n                val versionCode = packageInfo.versionCode\n\n                val integrityParams = IntegrityParams(\n                    packageName = PackageNameWrapper(packageName),\n                    versionCode = VersionCodeWrapper(versionCode),\n                    nonce = nonceArr.encodeBase64(noPadding = false, noWrap = true, urlSafe = true),\n                    certificateSha256Digests = packageInfo.signaturesCompat.map {\n                        it.toByteArray().sha256().encodeBase64(noPadding = true, noWrap = true, urlSafe = true)","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/vending-app/src/main/kotlin/com/google/android/finsky/integrityservice/IntegrityService.kt#L104-L140","documentation":"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.","triggerScenarios":"Calling requestIntegrityToken with a KEY_NONCE byte array whose size < 16, e.g. a short string converted to bytes or a truncated hash.","commonSituations":"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.","solutions":["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).","Check nonceArr.size >= 16 at the call site before sending the request.","If using a hash, do not truncate it — keep the full digest bytes.","Ensure any Base64 decode of the nonce is correct so it yields the expected length."],"exampleFix":"// before\nval nonce = System.currentTimeMillis().toString().toByteArray() // ~13 bytes\n// after\nval nonce = ByteArray(32).also { SecureRandom().nextBytes(it) } // or MessageDigest SHA-256 of request data","handlingStrategy":"validation","validationCode":"fun isNonceTooShort(nonce: ByteArray) = nonce.size < 16\nif (isNonceTooShort(nonce)) throw IllegalArgumentException(\"Nonce must be >= 16 bytes\")","typeGuard":"fun ByteArray?.isNonceValid(): Boolean = this != null && size in 16..499","tryCatchPattern":"try {\n    integrityManager.requestIntegrityToken(request)\n} catch (e: StandardIntegrityException) {\n    if (e.errorCode == IntegrityErrorCode.NONCE_TOO_SHORT) {\n        regenerateNonceAndRetry()\n    } else throw e\n}","preventionTips":["Generate nonces with SecureRandom or a full SHA-256 digest, never short IDs","Assert nonce length (16..499) in a shared helper before every call","Never truncate hashes used as nonces"],"tags":["play-integrity","kotlin","android","nonce","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}