google / google/play-integrity-unity

Decrypt and verify the integrity token standard or token classic failed wrong number of segments in token

Open
#1 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
14
Forks
3
PR merge metrics
No merged PRs in 30d

Description

I have a problem for decrypt token from StandardIntegrityToken in Unity and back-end use Firebase function. Code in unity:

```
public async UniTask<(string, string)> RequestIntegrityToken()
{
return await PrepareIntegrityTokenAsync();
}

private async UniTask<(string, string)> PrepareIntegrityTokenAsync()
{
// Create an instance of a standard integrity manager.
var standardIntegrityManager = new StandardIntegrityManager();

// Request the token provider.
var integrityTokenProviderOperation =
standardIntegrityManager.PrepareIntegrityToken(
new PrepareIntegrityTokenRequest(CLOUD_PROJECT_NUMBER));

// Wait for PlayAsyncOperation to complete.
await integrityTokenProviderOperation.ToUniTask();

// Check the resulting error code.
if (integrityTokenProviderOperation.Error != StandardIntegrityErrorCode.NoError)
{
Debug.LogError("StandardIntegrityAsyncOperation failed with error: " +
integrityTokenProviderOperation.Error);
return (null, null);
}

// Get the response.
var integrityTokenProvider = integrityTokenProviderOperation.GetResult();
string randomString = IntegrityTokenHelper.GenerateRandomString(20);
string expectedRequestHash = IntegrityTokenHelper.GenerateRequestHash(randomString);

var integrityTokenOperation = integrityTokenProvider.Request(new StandardIntegrityTokenRequest(expectedRequestHash));

await integrityTokenOperation.ToUniTask();

if (integrityTokenOperation.Error != StandardIntegrityErrorCode.NoError)
{
return (null, null);
}

// Get the response.
var integrityToken = integrityTokenOperation.GetResult();
return (integrityToken.Token, expectedRequestHash);
}
```
Code in firebase function:

```
const axios = require("axios");
const { OAuth2Client } = require("google-auth-library");
const auth = new OAuth2Client({
keyFile: 'credentials/service-account-file.json',
scopes: ['https://www.googleapis.com/auth/cloud-platform']
});
exports.verifyAndSaveData = onCall(
{ enforceAppCheck: true },
async (request) => {
if (!request.app) {
return { success: false, banReason: "App check failed" };
}

try {
const token = request.data.token;
const expectedRequestHash = request.data.expectedRequestHash;
const userId = request.data.userId;

const existingPlayerAuthData = await fetchPlayerAuthData(userId);
if (existingPlayerAuthData && existingPlayerAuthData.is_banned) {
return { success: false, banReason: existingPlayerAuthData.ban_reason };
}

const client = await auth.getClient();
const accessToken = await client.getAccessToken();

const url = `https://playintegrity.googleapis.com/v1/${expectedPackageName}:decodeIntegrityToken`;
const response = await axios.post(
url,
{ integrity_token: token },
{ headers: { Authorization: `Bearer ${accessToken.token}` } }
);

if (!response.data) {
return { iserror: true, success: false, banReason: "Decode Integrity Token Failed" };
}

let isBanned = false;
let banReason = "";

const payload = response.data;
const requestDetails = payload.requestDetails;
const requestPackageName = requestDetails.requestPackageName;
const requestHash = requestDetails.requestHash;
const timestampMillis = requestDetails.timestampMillis;
const currentTimestampMillis = Date.now();

if (requestPackageName !== expectedPackageName
|| requestHash !== expectedRequestHash
|| currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS)
{
isBanned = true;
banReason = "Fake token integrity";
}
else
{
const appIntegrity = payload.appIntegrity.appRecognitionVerdict;
const deviceIntegrityArray = payload.deviceIntegrity.deviceRecognitionVerdict || [];
const licensingVerdict = payload.accountDetails.licensingVerdict;

if (appIntegrity !== "PLAY_INTEGRITY_VERDICT_OK") {
isBanned = true;
banReason = "App integrity check failed";
}

if (deviceIntegrityArray.includes("MEETS_DEVICE_INTEGRITY")) {
isBanned = true;
banReason = "Device integrity check failed";
}

if (licensingVerdict !== "LICENSED") {
isBanned = true;
banReason = "App not licensed";
}
}

const playerAuthRef = admin.firestore().collection("player_auth").doc(userId);
if (existingPlayerAuthData) {
await playerAuthRef.update({
is_banned: isBanned,
ban_reason: isBanned ? banReason : "",
lastChecked: admin.firestore.FieldValue.serverTimestamp(),
});
}

if (isBanned) {
return { success: false, banReason: existingPlayerAuthData.ban_reason };
}

return { success: true, banReason: "" };
} catch (error) {
return { iserror: true, success: false, banReason: error.message };
}
}
);
```

I use documents from this source: [](https://developer.android.com/google/play/integrity/standard)

And I got the error:
`Wrong number of segments in token: CtsCARCnMGsxYSAjH2rAlbYYJlUvq4CGkyb_M154Merg6GnC-wdTMcjSibJwOOerVlYtQ3kpiVZgwM2_rHYXDbvUxCfp9Uvh40Ie8L3Z7_S98ZH1c0_91OS-oECtcylE7kLkKtv53ibOfIKjKxYl_iBfdx1Ilq4bTJhQy2kFEJd8Q2lp-MQWuSyGgR6mmVVH4uKnixvjLQv8WU_Qmt8WiaajNtkkTBdxwQWVU0OlqE1SwqMLmnifdDPbfJvhMgaV7cIWWe5Q00qJZc3nSKmyST8_M_kFGm_G4FC4kWT1o_NrIAbgaSuFXGyWUTfL38UhFTGRg74p8psW3RRA5Do9hUDlImdcM-jICx1qtU11yE5afYZukrOAGklyAiUUUnbtNdp_yfyw--98NK5_wuKQj906t9iSQ0ni0DK5axN9oBfab3b4k9EvBw3lRkXk-DhgRaBZ-0RsJNHhlXGlXC4afgHMcfsFFjTZ8B79lFbwTjiFIU8RogAqvQEf2B9blQaF9uUWxP-AXjFNeXEgCYhFtAAb0ac7s5WGX3sifRjPf4vU7DddT1Y1613y4o9G-6dgGLKy2dFS4-irlo5c0rIyfQbumIlcB6Yfxx9oQhUIdmM8G8X0TRQVUMNl8JJHzg`

I tried switching to classic request and it gave me the same error.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.