Photo by Mackenzie Marco on Unsplash

App Attestation with Play Integrity: Verdicts for Multi-Point Inspection (Part 1)

Mayank Mehta

--

In this two-part series, we’ll go over how to leverage Google’s Play Integrity API for app attestation. In part one, we will discuss why we should invest in app attestation, how to generate an Integrity Token, why Message Integrity is crucial, and finally how to interpret Integrity Token verdicts.

Why should you learn about App Attestation?

In a world where we are focused on launching customer-facing features to get the competitive edge and lock user’s attention, it is equally important to invest in non-ui features to keep our customers safe. Lets run through few questions, would we like that our customers are:

  1. Interacting with a genuine app binary that is recognized by Google Play?
  2. Running the app on a genuine Android device powered by Google Play services / games?
  3. Running the app on a device that does not have malware/harmful apps?

If you answered YES to any of the questions above, this series of articles will explain how we can leverage the Play Integrity API to keep our consumers safe.

How does this work?

We have two components to consider:

  1. Play Integrity Android Library: In the app we will use this library to generate an Integrity Token (which is encrypted) to protect a user action. We will send this token to our server along with the request we are trying to protect.
  2. In our secure server — we would decrypt the Integrity Token, either locally (by self managed keys) or by making calls to Google Play Integrity API . Once a token is decrypted we get a JSON response that has verdicts, we will verify the information and make a decision on how we want to handle the request.

Apple has a similar concept of App Attestation, however Android Play Integrity Token verdicts are comprehensive and help make an educated decision.

Integrity Token Generation

The Play Integrity Android Library offers two options for creating Integrity Tokens: Standard Request and Classic Request. Depending on the use case, we might select any request type. The following pros and cons for each request type will help you make an informed selection.

Standard Request:

Pros:

  1. Low latency.
  2. To optimize, we can initialize the token provider during app launch and generate a new token on demand for each user action we wish to protect.
  3. Only token decryption counts toward the project’s rate limit

Cons:

  1. Some verdicts could be cached. Compromise for low latency.
  2. The request hash limit is 500 bytes.
  3. Token decryption requires making a call to Google Cloud Play Integrity API. It creates a hard dependency on an external service.

Classic Request:

Pros :

1. All verdicts are re-computed.

2. Token decryption can be done on our secure server. However, we would be responsible for securing decryption and signature validation keys. While this is an option, Google recommends to get the token decrypted using its API.

Cons:

1. High latency and expensive (in terms of data and battery usage).

2. Should be used for one-off checks.

3. Token generation and decryption is counted towards the project’s rate limit.

🛑 Play Integrity API has a 10K rate limit for total request per day for each app. You can submit an intake request here to be considered for higher rate limit tier.

Message Integrity and Replay Protection

The fundamental principle of message integrity is to ensure that our request is not tampered with, while it gets sent from our client to the server. When we generate an integrity token, we should associate it with elements of the request that we are protecting with the integrity token.

With replay protection, we ensure that an integrity token is not replayed from the same device, another device, or for a different request.

The code example below demonstrates how to associate a request with an integrity token using a standard token provider.

standardIntegrityTokenProvider.request(
StandardIntegrityTokenRequest.builder()
.setRequestHash(requestHash)
.build()
)

On our backend service, after decrypting the token, we perform a message integrity check on the incoming request by calculating the hash and comparing it to the request hash returned in the decrypted integrity token.

🤝 Clients and the backend service must agree on which fields will be included in the request hash and how the hash will be generated.

Things to consider while creating a request hash:

  1. The play integrity API has a 500 bytes limit on request hash size for Standard Requests. As a result, instead of hashing the entire request, specific attributes from it may be looked at.
  2. Consider binding some device specific information such that the token is not replayed from another device.
  3. To provide replay protection, a nonce (number used only once) can be added.

🤞 The Request Hash that we pass to the Token Provider should NEVER contain information in plain text.

Google Play Integrity API offers replay protection. As is, if the same token is decrypted several times, it will return an empty response. Based on our risk tolerance, we may need to create our own replay protection for tighter controls.

Integrity Token Verdicts

The play integrity token gives a comprehensive collection of verdicts from which we can make decisions based on our risk profile. The decrypted integrity tokens contain the following response.

Let us have a brief look at how each particular check should be used.

1. Request Details: This field can be used to ensure that the request is coming from the app that requested attestation, as well as to check message integrity by utilizing the request hash and time stamp to determine whether the token is stale.

2. App Integrity: This field can be used to check whether the app is recognized by Google Play.

3. Device Integrity: This field can be used to identify whether the app is running on a device that supports Google Play services, whether the device passes the integrity check (no API hooking or rooting), whether the device is running a recognized Android version, and whether the device has hardware-backed boot integrity.

4. Account Details: This field can be used to establish whether the software was purchased or installed via Google Play.

5. Environment Details (Optional): This field can be used to see if Play Protect is enabled and whether the scan discovered any harmful or risky apps.

💡 Integrity Token verdicts (after decrypting the token) follow the same structure for both standard and classic request types.

Optional fields within token verdicts can be configured for each application on Google Play Console.

If you’ve made it this far, thank you, and I hope you learned something. In Part 2, we’ll go over how to create our backend attestation service to decode the integrity token. Happy learning!

--

--