Skip to main content

Trust API Bind (TAB) Integration Documentation

1. Android SDK Integration Guide

This guide details how to integrate the zrt-release.jar local SDK into your Android application, generate secure cryptographic tokens, and properly transmit them to protect your critical business infrastructure.

Phase 1: Project Setup & Installation

1. Place the JAR in the Project Directory

First, add the .jar file to your app's local libs directory so it is bundled with the project.

Navigate to your project structure to your Android app module (usually named app). If it doesn't already exist, create a folder named libs inside the app folder. Move zrt-release.jar into this directory.

Expected Structure:

your_project/
├── app/
│ ├── libs/
│ │ └── zrt-release.jar
│ ├── src/
│ └── build.gradle (.kts)

2. Configure Repositories

If you are using modern Android Studio (Android Gradle Plugin 7.0+), dependency repositories are centralized in your settings file. You need to declare the flatDir pointing to the libs directory.

note

In some legacy projects, repositories are defined in the root build.gradle inside allprojects { repositories { ... } }.

Using Kotlin DSL (settings.gradle.kts):

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// Define the local library directory
flatDir {
dirs("libs")
}
}
}

Using Groovy (settings.gradle):

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// Define the local library directory
flatDir {
dirs 'libs'
}
}
}

3. Add Dependency

Next, declare the JAR file as an implementation dependency in your app-level build configuration (app/build.gradle or app/build.gradle.kts). Follow the explicit file dependency strategy.

Using Kotlin DSL (app/build.gradle.kts):

dependencies {
// Other dependencies...

// Explicitly add the ZRT SDK
implementation(files("libs/zrt-release.jar"))
}

Using Groovy (app/build.gradle):

dependencies {
// Other dependencies...

// Explicitly add the ZRT SDK
implementation files('libs/zrt-release.jar')
}
important

Sync your Gradle files after making these changes.


Phase 2: Generating the Token

Once the Gradle sync completes, you are ready to import and invoke the SDK. The ZeroRootTrust class resides in the com.zrt package and exposes a static method getToken(String parameter).

4. SDK Invocation

Kotlin (MainActivity.kt):

package com.your.package.name

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.zrt.ZeroRootTrust

class MainActivity : AppCompatActivity() {
private val TAG = "ZRT-Integration"

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// Replace with the specific data payload/nonce fetched from your server
val dataPayload = "server_generated_nonce_123"

// Invoke the ZRT SDK function
val zrtToken = ZeroRootTrust.getToken(dataPayload)

Log.d(TAG, "Successfully generated ZRT Token: $zrtToken")
}
}

Java (MainActivity.java):

package com.your.package.name;

import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import com.zrt.ZeroRootTrust;

public class MainActivity extends AppCompatActivity {
private static final String TAG = "ZRT-Integration";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// Replace with the specific data payload/nonce fetched from your server
String dataPayload = "server_generated_nonce_123";

// Invoke the ZRT SDK function
String zrtToken = ZeroRootTrust.getToken(dataPayload);

Log.d(TAG, "Successfully generated ZRT Token: " + zrtToken);
}
}

5. Understanding the Cryptographic Nonce

When invoking getToken(), you must pass a string parameter (dataPayload). This acts as a cryptographic nonce (number used once) and plays a critical role in the Zero Root Trust process.

  • Prevention of Replay Attacks: The nonce ensures that every generated token is uniquely tied to a specific session or server request. Malicious actors cannot intercept a valid token and successfully reuse it later, because the outdated nonce bound to that token will be rejected by your backend.
  • Cryptographic Binding: When you pass the nonce into the SDK, it is securely embedded and signed into the generated ZRT token's payload.

Possible Formats for Nonce:

The nonce parameter is flexible and can represent different forms of contextual integrity data depending on your security requirements and backend implementation. Common approaches include:

FormatDescription
Server-Generated NonceA unique, cryptographically secure nonce fetched from your backend for every sensitive request or session.
Checksum of API Request PayloadA checksum generated from the API request body or critical transaction parameters.
Custom Validation StringAny custom string representing data important for integrity verification during testing or implementation.

Best Practices:

  • Dynamic & Unique: Must be generated dynamically by your backend server for each new sensitive action.
  • Unpredictable: Generated using a cryptographically secure random number generator (CSRNG).
  • Time-bound: The server should associate a strict expiration time with the nonce (e.g., 60 seconds).

Phase 3: Transmission & Endpoint Security

6. Transmitting the Token via HTTP Headers

To ensure your backend can seamlessly verify the integrity of the request, do not send the ZRT token in the request body or URL parameters. It must be attached as a standard HTTP Header on your outgoing network requests (e.g., via Retrofit, OkHttp, or Volley).

We recommend using a dedicated custom header to avoid conflicts with standard OAuth/Session tokens:

  • Header Name: X-ZRT-Token
  • Header Value: <the_generated_zrt_token>

Example — OkHttp Interceptor:

val zrtInterceptor = Interceptor { chain ->
val originalRequest = chain.request()

// Generate token dynamically right before the network call
val token = ZeroRootTrust.getToken(currentNonce)

val newRequest = originalRequest.newBuilder()
.header("X-ZRT-Token", token)
.build()

chain.proceed(newRequest)
}

7. Securing Critical Business Logic

A Zero-Trust architecture assumes the client device is inherently compromised. To properly protect your business, you must enforce ZTRB token validation on as many high-risk endpoints as possible.

The Golden Rule

If a specific API endpoint modifies data, moves money, or grants access, it should require a fresh ZTRB token generated using a fresh, server-issued nonce.

Require a fresh nonce and newly generated ZRT token for all of the following:

Authentication & Onboarding

  • Account creation and login endpoints
  • Password resets and MFA verification steps

Financial & Transactional Logic

  • Checkout processing, wallet top-ups, and fund transfers
  • Adding or modifying payment methods

Profile & PII Mutations

  • Updating email addresses, phone numbers, or physical addresses
  • Viewing or exporting sensitive PII data

Core App Economy

  • Claiming rewards, redeeming promo codes, or executing high-value in-app actions

2. iOS Integration Guide

This guide details how to integrate the ZRTAttest.xcframework into your iOS application to enable secure device attestation, generate cryptographic tokens, and properly transmit them to protect your critical business infrastructure.

Phase 1: Project Setup & Installation

Prerequisites

RequirementVersion
Xcode14.0 or later
iOS Deployment Target13.0+
FrameworkZRTAttest.xcframework (provided by Bugsmirror)

1. Add the XCFramework to Your Project

  1. Open your Xcode project.
  2. Drag ZRTAttest.xcframework into the Project Navigator panel (left sidebar).
    • Alternatively: Go to File → Add Files to "[YourProject]"... and select the .xcframework.
  3. In the dialog that appears, make sure "Copy items if needed" is checked.
  4. Click Finish.

2. Embed the Framework

For the SDK to load properly at runtime, it must be embedded in your app's build phase.

  1. Select your app target in the Project Navigator.
  2. Go to the General tab.
  3. Scroll down to the "Frameworks, Libraries, and Embedded Content" section.
  4. If ZRTAttest.xcframework is not already listed, click the "+" button and add it.
  5. Set the Embed option to "Embed & Sign".

Phase 2: Generating the Token

3. SDK Invocation

Import the framework and invoke the ZRTAttestManager to generate a token.

API Reference:

PropertyDetail
MethodZRTAttestManager.generateToken(nonce: String) -> String
Parametersnonce (String) — A unique, one-time-use string provided by your backend server
Return ValueA signed JWT string (e.g., eyJhbGciOi...) on success, or an empty string "" on failure

Example (ViewController.swift):

import UIKit
import ZRTAttest

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

// 1. Fetch a fresh nonce from your backend server
let nonce = "server_generated_nonce_12345"

// 2. Generate the attestation token
let token = ZRTAttestManager.generateToken(nonce: nonce)

// 3. Validate generation success
if !token.isEmpty {
print("Successfully generated ZTRB Token: \(token)")
// Proceed to attach token to your outgoing API request
} else {
print("Token generation failed.")
}
}
}

4. Understanding the Cryptographic Nonce

When invoking generateToken(), you must pass a nonce (number used once). This string plays a critical role in the integrity of the Zero Root Trust process.

  • Prevention of Replay Attacks: The nonce ensures that every generated token is uniquely tied to a specific session or server request. Malicious actors cannot intercept a valid token and successfully reuse it later, because the outdated nonce bound to that token will be rejected by your backend.
  • Cryptographic Binding: When you pass the nonce into the SDK, it is securely embedded and signed into the generated ZTRB token's payload.

Possible Formats for Nonce:

FormatDescription
Server-Generated NonceA unique, cryptographically secure nonce fetched from your backend for every sensitive request or session.
Checksum of API Request PayloadA checksum generated from the API request body or critical transaction parameters.
Custom Validation StringAny custom string representing data important for integrity verification during testing or implementation.

Best Practices:

  • Dynamic & Unique: Must be generated dynamically by your backend server for each new sensitive action.
  • Unpredictable: Generated using a cryptographically secure random number generator (CSRNG).
  • Time-bound: The server should associate a strict expiration time with the nonce (e.g., 60 seconds).

Phase 3: Transmission & Endpoint Security

5. Transmitting the Token via HTTP Headers

To ensure your backend can seamlessly verify the integrity of the request, do not send the token in the request body or URL parameters. It must be attached as a standard HTTP Header on your outgoing network requests (e.g., via URLSession, Alamofire, or Moya).

We recommend using a dedicated custom header to avoid conflicts with standard OAuth/Session tokens:

  • Header Name: X-ZRT-Token
  • Header Value: <the_generated_zrt_token>

Example — URLSession:

func makeSecureAPIRequest(nonce: String) {
// Generate token dynamically right before the network call
let token = ZRTAttestManager.generateToken(nonce: nonce)

guard let url = URL(string: "https://api.yourdomain.com/v1/secure-endpoint") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"

// Attach the ZTRB Token
request.setValue(token, forHTTPHeaderField: "X-ZRT-Token")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

let task = URLSession.shared.dataTask(with: request) { data, response, error in
// Handle response
}
task.resume()
}

6. Securing Critical Business Logic

A Zero-Trust architecture assumes the client device is inherently compromised. To properly protect your business, you must enforce ZTRB token validation on as many high-risk endpoints as possible.

The Golden Rule

If a specific API endpoint modifies data, moves money, or grants access, it should require a fresh ZTRB token generated using a fresh, server-issued nonce.

Do not limit token validation to just the login screen. You should require a fresh nonce and a newly generated ZTRB token for all of the following critical pathways:

Authentication & Onboarding

  • Account creation and login endpoints
  • Password resets and MFA verification steps

Financial & Transactional Logic

  • Checkout processing, wallet top-ups, and fund transfers
  • Adding or modifying payment methods

Profile & PII Mutations

  • Updating email addresses, phone numbers, or physical addresses
  • Viewing or exporting sensitive PII data

Core App Economy

  • Claiming rewards, redeeming promo codes, or executing high-value in-app actions that are frequent targets for botting and automation.

Troubleshooting Reference

IssuePotential CauseSolution
Token is emptyDefender SDK not loaded at runtimeEnsure the Defender binary is properly injected into the app environment before token generation is called
Module 'ZRTAttest' not foundFramework not embedded correctlyVerify Phase 1, Step 2 — ensure "Embed & Sign" is explicitly selected in the target settings
Build error on simulatorMissing simulator slice in the binaryEnsure the .xcframework provided was built with the -dev flag for local simulator support

3. Backend Integration Guide

This guide outlines the steps required to integrate the Trust API Bind (TAB) validation into your backend infrastructure. This feature dynamically extracts the X.509 certificate chain from incoming JWTs, verifies the cryptographic signature against the Bugsmirror Root CA, and enforces strict temporal binding to prevent replay attacks and time manipulation.

TAB can be integrated into your backend ecosystem by either using a Docker container or an embedded native module, enabling options best aligned with your infrastructure.

3.1: Modern OnPrem Deployment

In this approach, we provide binaries compiled for various ABIs , alongside a pre-configured Dockerfile as a Template. The Dockerfile is responsible for initiating the service hosted in your internal cloud network by triggering the appropriate binary opted as per your infrastructure. The TAB binaries are available on the MASST portal and below is the template of Dockerfile.

3.1.1 Container Architecture & Deployment steps

The containerised service runs an isolated lightweight Linux execution environment. At container launch, the TAB binary gets executed starting the service responsible for handling TAB token validation requests.

FROM alpine:latest

RUN apk add --no-cache libc6-compat

WORKDIR /app

# Ensure to select the validation binary matching your system architecture (Linux or macOS)
COPY tab-validator-linux-amd64 ./tab-validator-linux-amd64


COPY assets/ /app/assets/


RUN chmod +x ./tab-validator-linux-amd64

ENV TAB_PORT=8080 \
TAB_ENV=cloud \
ROOT_CERT_FILE_PATH=/app/assets/rootCA.pem \
SERIAL_ID_FILE_PATH=/app/assets/serialId.json


EXPOSE 8080

ENTRYPOINT ["/app/tab-validator-linux-amd64"]

note

Please ensure you choose the validation binary corresponding to your specific system architecture (e.g., Linux x86/ARM, macOS ARM/x86).

For Building and uploading docker images to the cloud execute the following command.

docker build -t <REGISTRY_NAME> .
docker push <REGISTRY_NAME>

Here REGISTRY_NAME holds the unique web address of the cloud storage server where your compiled Docker container images are stored.

3.1.2 Environment Variables for the DockerFile

ArgumentDescription & Purpose
ROOT_CERT_FILE_PATHThe file path to the PEM-encoded public key/certificate.
SERIAL_ID_FILE_PATHPath to the JSON registry containing known revoked hardware identifiers.
TAB_PORTNetwork port on which the TAB validator binary will listen on
TAB_ENVDeployment Type(cloud or module Based) for cloud deployment case the value will be "cloud" and in module case it can be empty.

3.1.3 Request and Response Execution Steps

A. Token Validation Endpoint (POST /tab)

When deployed in container environments (AWS ECS, GCP Cloud Run, Azure Container Apps, or Kubernetes), applications interact with the containerized validator engine by issuing HTTP POST requests to /tab.

1. Headers Required:

Header NameTypeDescription
x-ztrb-tokenStringThe raw incoming TAB JWT token extracted from the client's HTTP request header. The TAB 2.0 token size varies from 10 KB to 13 KB depending on the nonce size.

2. Container Request Payload (POST /tab) JSON Request Body Example

{
"PackageName": "com.example.pkg",
"ExpiryDurationSeconds": 300,
"AllowedClockDriftSeconds": 5,
"SessionDurationSeconds": 86400,
"Platform": 0,
"AppIdentifier": [
"A1:B2:C3:D4:E5:F6:78:90:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF"
],
"EnforceBState": true,
"EnforceCertificateExpiry": false
}

Parameter Definitions

ParameterTypeDescription & Security Purpose
PackageNameStringUnique application identifier (Android Package Name or iOS Bundle ID). Prevents token reuse across different apps.
ExpiryDurationSecondsint64Maximum allowed token age (in seconds). Limits window of vulnerability for stolen tokens.
AllowedClockDriftSecondsint64Maximum client-server clock skew tolerance (in seconds). Prevents clock manipulation.
SessionDurationSecondsint64Maximum allowed session validity duration (in seconds) for evaluating active token session lifetime. The Recommendation would be 5 min that is 300 seconds
Platformint64OS Platform identifier (0 = Android, 1 = iOS). Enforces platform-specific certificate checks.
AppIdentifierArray[String]Allowed app signing certificate SHA-256 hashes (Android) or Team IDs (iOS). Must set the specific app identifier for the corresponding platform
EnforceBStateBooleanEnforces hardware backed server side attestation
EnforceCertificateExpiryBooleanWhen set to true, validates expiration dates of leaf and intermediate X.509 certificates. Only be enforced if b_state enforcement is opted.
Note: Since certain devices still contain expired Google certificates, this might impact some genuine users. It is recommended to enforce the EnforceCertificateExpiry check only after further analysis and based on business requirements.

Response Payload

{
"TabResult": {
"IsRevoked": false,
"Nonce": "valid-nonce-123",
"VState": "Passed",
"Pkg": "com.example.pkg",
"IssuedAt": 1785604247,
"ClockValue": 0,
"IsCertExpired": false,
"Res": 0
},
"TabError": {
"Msg": ""
}
}
note

InCase of nonEmpty Tab error, Response status code will be 401 (Unauthorized)

Sample cURL Command

curl -X POST "http://your-server-url/tab" \
-H "x-ztrb-token: eyJhbGciOiJSUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"PackageName": "com.example.pkg",
"ExpiryDurationSeconds": 300,
"AllowedClockDriftSeconds": 5,
"SessionDurationSeconds": 86400,
"Platform": 0,
"AppIdentifier": [ "A1:B2:C3:D4:E5:F6:78:90:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF"
],
"EnforceBState": true,
"EnforceCertificateExpiry": false,
}
B. Hardware Revocation List Reload Endpoint (POST /reload)

Reloads the revoked hardware serial IDs from the SERIAL_ID_FILE_PATH JSON file into memory, eliminating the need for a service restart. It is designed to handle multiple concurrent calls safely.

Endpoint: POST (http://your-server-url/reload)

Request Body: Empty body.

HTTP Response Codes & Error Messages:
ResponseCodeError Message
200 OKHardware serial ID revocation list reloaded successfully.
405 Method Not AllowedThe HTTP method is not POST. Returns JSON error "msg": "INVALID-method not allowed".
429 Too Many RequestsConcurrent reload request already in progress. Returns JSON error "msg": "InternalError-too many reload requests in progress".
500 Internal Server ErrorFailed to read or parse serialId.json file on disk. Returns JSON error "msg": "InternalError-failed to reload serial ids".
Response Body (JSON on Error):
{
"TabResult": {},
"TabError": {
"Msg": "InternalError-failed to reload serial ids"
}
}

Enforcement of b_state claim:

b_State Error ContainsDescriptionAction/remark
b_state is missingThe b_state value is missing in the token. This typically occurs because hardware attestation is not supported on the device, the application is in a fresh initiating state where security signals are not yet available, or a transient exception occurred during the attestation process.For high-risk operations (such as financial transactions, account recovery, or sensitive data access), the request should be denied or challenged. In instances where the b_state claim is absent, we advise initiating an exponential backoff retry mechanism for a maximum duration of 30 seconds.
b_state verification failedThis acts as a primary security alert. All the specific reasons (such as failing to parse the certificate, detecting a malicious device, tampered security credentials, or found serial id mismatch) are the underlying technical triggers that lead to the token being rejected.Block Request: Integrity validation failed due to invalid, mismatched, or tampered security credentials.
Note

The b_state value will be empty for the following cases:

  • if the '00000' threat is detected
  • if the Android version is below 10.

You can extract the OS version at server-side using the Google Play Integrity API. However, deviceAttributes must be enabled in the Google Play Console for OS details to appear in your backend verdict payload.

Enforcement of v_state claim:

This v_state can be used to determine legitimacy of the environment in which the application is running as long as the underlying defender vm is intact.

Note

If the v_state claim is present as Passed or Trusted, it is recommended to verify the isRevoked boolean flag. Accept the request payload only if isRevoked is false (indicating the device serial ID is not revoked). If isRevoked is true, the request should be rejected.

V_STATE ValuesDescriptionRemark/Action
UnknownThe Defender cannot establish the device's current status. This typically arises from factors such as the device lacking essential secure hardware, the application operating on a version prior to Android 10, or the Defender still being in the process of evaluating the environment.For high-risk operations (such as financial transactions, account recovery, or sensitive data access), the request should be denied or challenged. If devices present an unknown v_state, we suggest applying an exponential backoff retry strategy for up to 30 seconds to allow for status resolution.
FailedThe Defender has determined that the device does not meet the required integrity or trust criteria. This may indicate a compromised, tampered, emulated, or otherwise untrusted execution environment.The backend should deny the request and prevent access to protected functionality. Additional monitoring or fraud controls may be applied according to organizational policy.
PassedThe Defender has validated the device using standard integrity signals and determined that the device appears legitimate. This assessment is based on the integrity information available at the time of evaluation.Suitable for most commercial applications and standard business workflows where a balance between security, user experience, and device coverage is desired. The server may accept requests and apply standard risk controls.
TrustedThe Defender has validated the device using the strongest available integrity signals, providing a higher level of confidence in the device's authenticity and execution environment compared to the Passed level. This assessment depends on the integrity and security of the underlying attestation mechanisms and platform signals.Intended for applications requiring the highest level of device trust. Because Trusted relies on stronger integrity requirements, it may reduce the number of eligible devices compared to Passed. Use this level for high-risk operations, sensitive transactions, privileged actions, or environments where enhanced security is prioritized over maximum device coverage.
Note

Retry the request under either of the following conditions:

  • v_state is Unknown
  • enforce b_state is enabled and the error message contains "b_state is missing"

3.2: Legacy Module Integration Guide

3.2.1 Required Dependencies

To handle JWT parsing, X.509 certificate extraction securely, protocol buffers and grpc dependencies, you must add the enterprise-standard libraries to your project based on the programming language.

3.2.2 The Core Validation Module Setup and Configuration Guide

Import and integrate the tab validation module in your project. Call the InitValidator function only once in the main function of your server with the following parameters.

Argument Definitions & Security Implications

ArgumentTypeDescription & Purpose
rootCertFilePathStringThe file path to the PEM-encoded public key/certificate.
serialIdFilePathStringPath to the JSON registry containing known revoked hardware identifiers.
tabBinaryPathStringFile path to the executable TAB validator binary.
portintNetwork port on which the TAB validator binary will lis
note
  1. Please ensure you choose the validation binary corresponding to your specific system architecture (e.g., Linux x86/ARM, macOS ARM/x86).
  2. The ROOT_CA_PEM must be stored into a secure environment variable or Secret Manager.
  3. Before calling the initvalidator function make sure the binaries have executable permissions.
  4. If you are deploying this legacy module using Docker, ensure the TAB validator binary is copied into your Docker image (e.g., using COPY) and that the correct path to this binary within the container is provided as the tabBinaryPath argument when calling InitValidator.
  5. The TAB sidecar binary exposes internal HTTP endpoints on the designated port: /tab (for token validation) and /reload (for dynamic revocation list reloads).

3.2.3 Validation Process

The globally initialised validator can be passed into middleware or directly into APIs. On calling the validate() function from the initialised validator, nonce, revocation state of the device, clock value, etc. being extracted, every incoming request gets secured and following claims get validated.

note

Enforce the status and error messages returned by the validate function into standard API responses.

validate() Method Signature & Arguments

ArgumentTypeRequiredDescription
tokenStringStringYesRaw TAB JWT string extracted from incoming request header. The TAB 2.0 token size varies from 10 KB to 13 KB depending on the nonce size.
packageNameStringYesUnique package name of the target app (Android Package Name or iOS Bundle ID).
expiryDurationSecondsint64YesMaximum allowed token age in seconds (Recommended: 60).
allowedClockDriftSecondsint64YesMaximum clock drift tolerance in seconds (Recommended: 5).
sessionDurationSecondsint64YesMaximum allowed active session duration in seconds (Recommended: 300).
enforceBStatebooleanoptionalEnforces hardware backed server side attestation (b_state).This field is accessible in CLI 465 and above versions.
enforceCertificateExpirybooleanoptionalEnforces expiration check on leaf/intermediate X.509 certificates. Only be enforced if b_state enforcement is opted
platformint64YesPlatform OS identifier (0 = Android, 1 = iOS).
appIdentifiersArray[String]Conditional(will be required when enforcing b_state)Allowed app signing certificate SHA-256 hashes (Android) or Team IDs (iOS). Must set the specific app identifier for the corresponding platform

Claims Definitions & Security Implications:

ArgumentTypeDescription & PurposeSecurity Implication
nonceStringA unique, one-time-use string embedded within the token to ensure request uniqueness.Prevents "Replay Attacks" where a legitimate request is intercepted and resent by an adversary.
pkgStringThe unique application identifier string is used to validate whether requests from the specified application.Request from genuine application is crucial. Mismatch of this string will cause the cryptographic signature check to fail instantly.
iatlongTime-based claims indicating when the token was created and when it becomes invalid.Strict Replay Protection. By keeping this window small (60 seconds), you drastically reduce the risk profile if an attacker successfully intercepts a token in transit.
v_stateStringA status claim indicating the security posture of the originating environment.This status indicator verifies the legitimacy of the request environment. Refer to the table below for a detailed breakdown of its possible values.
cert_serialsArrayThe unique hardware or certificate serial numbers associated with the device.Enables precise "Hardware Binding," ensuring the token is only valid when presented by the specific device to which it was issued.
b_stateArrayContains a high-assurance cryptographic trust proof that enables backend services to independently verify the authenticity of the requesting application and device. This claim can be used as an additional trust factor for sensitive operations, including transaction authorization, fraud prevention, adaptive access control, account protection, and step-up verification workflows. By validating this claim, backend systems can make stronger risk-based decisions regarding the legitimacy of a request and its execution environment.Support will be available in 465+ CLI releases.
note

Enforcement description of v_state and b_state is mentioned here.

3.2.4 Dynamic Revocation List Reload (ReloadSerialIds)

The ReloadSerialIds() function allows for seamless updates to the revoked hardware serial IDs registry (serialId.json) without interrupting service. When the revocation list on disk is modified, calling this function signals the active sidecar process to reload the file into memory, removing the need for an application or sidecar binary restart. Note that this function returns the same error codes and messages as those detailed in the On Prem Deployment Module.

Appendix

Implementation in Java

Required Dependencies

Add the following to your pom.xml:

<dependencies>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<!-- X.509 Certificate Validation (REQUIRED) -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<!-- JSON Parsing for Revoked Certs (REQUIRED) -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<!-- Protobuf Dependencies -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>4.35.1</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.75.0</version>
</dependency>
<!-- gRPC Protobuf integration -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.64.0</version>
</dependency>
<!-- gRPC Stubs (for the generated code) -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.64.0</version>
</dependency>
</dependencies>

Implementation Example

package com.example.tab;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.bugsmirror.TokenValidator;
import org.bugsmirror.TABServices.TABResult;
import org.bugsmirror.TABServices.TABError;
import org.bugsmirror.TABServices.ValidationResult;

public class Main {
private static final Logger logger = Logger.getLogger(Main.class.getName());

public static void main(String[] args) {
try {
// =========================================================================
// STEP 1: Initialize the TAB Validator Module
// =========================================================================
TokenValidator validator = new TokenValidator(
"./assets/rootCA.pem", // Bugsmirror Root CA PEM file path
"./assets/serialId.json", // Revoked hardware serial IDs JSON file path
"./tab-binaries/tab-validator-darwin-arm64", // TAB binary executable file path
8080 // Internal hosting port for TAB sidecar binary
);

// =========================================================================
// STEP 2: Extract Incoming Token from Request Header
// Retrieve the JWT string from client request header (e.g. x-ztrb-token)
// =========================================================================
String tokenString = "eyJhbGciOiJSUzI1Ni..."; // Replace with token fetched from client request header

// =========================================================================
// STEP 3: Perform TAB Token Validation
// Pass request token along with per-request security policies and parameters.
// =========================================================================
ValidationResult result = validator.validate(
tokenString, // token fetched from client
"com.example.pkg", // package name of the application
60, // token expiration time (seconds)
5, // clock drift duration (seconds)
true, // enforces binding state
true, // enforces device leaf certificate expiry
0, // application OS (0 = ANDROID, 1 = IOS)
300, // session duration (seconds)
new String[]{""} // app identifiers (App Team ID or SHA-256 signature)
);

TABResult tabResp = result.getResponse();
TABError tabErr = result.getError();

// 1. Check for TAB Errors
if (tabErr != null && tabErr.getMsg() != null && !tabErr.getMsg().isEmpty()) {
String errMsg = tabErr.getMsg();

// "SecurityAlert-b_state is missing"
if (errMsg.startsWith("SecurityAlert-b_state is missing")) {
// If attestation b_state is missing and v_state is Unknown,
// the client environment cannot be established yet. Issue a Retry challenge to client.
if (tabResp != null && "Unknown".equalsIgnoreCase(tabResp.getVState())) {
logger.info("[ACTION: RETRY] b_state is missing and v_state is UNKNOWN. Prompting client to re-attest and retry.");
// Response to client: Ask client app to re-generate attestation token and retry
return;
}
// If v_state is not Unknown, missing b_state indicates tampered payload.
logger.info("[ACTION: REJECT] b_state is missing and v_state is not Unknown. Rejecting request.");
// Response to client: HTTP 401 Unauthorized / Reject
return;
}

// "SecurityAlert-b_state verification failed"
// Hardware attestation/signature check failed (tampered app/device).
if (errMsg.startsWith("SecurityAlert-b_state verification failed")) {
logger.info("[ACTION: REJECT] b_state verification failed. Rejecting request.");
// Response to client: HTTP 401 Unauthorized / Reject
return;
}

// Other Errors (Refer to Error Specifications Table)
logger.log(Level.WARNING, "[ACTION: REJECT] TAB Error encountered ({0}). Rejecting request.", errMsg);
// Response to client: HTTP 401 Unauthorized / Reject
return;
}

// 2. No TAB Error -> Check isRevoked Status & Certificate Expiry
if (tabResp != null) {
// Hardware device ID is present in serialId.json blacklist. ALWAYS reject requests.
if (tabResp.isRevoked()) {
logger.log(Level.WARNING, "[ACTION: REJECT] Request rejected! Device hardware ID is REVOKED (Reason Code: {0}).", tabResp.getRes());
// Response to client: HTTP 403 Forbidden / Reject
return;
}

// Can Be Enforced based on Business Requirement: //Evaluates if any device attestation certificate in the chain has expired.
if (tabResp.isCertExpired()) {
logger.warning("[SECURITY WARNING] Device attestation certificate is EXPIRED (IsCertExpired = true).");
}

// 3. Is Nonce Validated?
// Validate that the one-time nonce has not been previously consumed in server session cache.
boolean isNonceValid = validateNonce(tabResp.getNonce()); // User Defined Function
if (!isNonceValid) {
logger.info("[ACTION: REJECT] Nonce validation failed (replay attempt or invalid nonce). Rejecting request.");
// Response to client: HTTP 401 Unauthorized / Reject
return;
}

// 4. All Security & Policy Checks Passed -> Allow User
logger.info("[ACTION: ALLOW USER] TAB 2.0 Validation Successful!");
logger.info(" • Nonce : " + tabResp.getNonce());
logger.info(" • Package Name : " + tabResp.getPkg());
logger.info(" • Security State : " + tabResp.getVState());
logger.info(" • Cert Expired : " + tabResp.isCertExpired());
logger.info(" • Issued At (iat): " + tabResp.getIssuedAt());
logger.info(" • Clock Drift : " + tabResp.getClockValue() + " seconds");

// Proceed with backend business logic for authorized user request...
}

// This step is conditional: initiate this reload only upon receiving an email update of serial ID data.
try {
validator.reloadSerialIds();
} catch (Exception e) {
logger.log(Level.WARNING, "Failed to reload serial IDs: {0}", e.getMessage());
}

} catch (Exception e) {
logger.log(Level.SEVERE, "Fatal: Failed to initialize TAB validator module: {0}", e.getMessage());
}
}
}

Error Specification

ConditionReturned Error (Message)
HTTP method not allowed“method not allowed”
Token string is missing from requestmissing token string
Request body is empty or malformedINVALID-Empty or invalid request body
Package name parameter missingMISSING-package name is missing
Expiry duration not providedMISSING-expiry duration seconds is missing
Clock drift seconds not providedMISSING-clock drift seconds is missing
App identifiers missingMISSING-missing application identifiers
Validator instance is nilMISSING-validator is nil
Required system config missingMISSING-system configuration Msg: missing required configuration parameters
Root CA PEM decode failedINVALID-system configuration Msg: failed to decode Root CA PEM
Root CA format invalidINVALID-system configuration Msg: invalid Root CA format
Token signature or chain validation failedMALFORMED-token signature/chain validation failed
'iat' (Issued At) claim missingSecurityAlert-missing 'iat' (Issued At) claim
Token timestamp is in the futureSecurityAlert-token claims to be from the future
Token has expiredUNAUTHORIZED-token expired
'nonce' claim missingSecurityAlert-missing 'nonce' claim
'v_state' claim missingSecurityAlert-missing required v_state claim
'pkg' claim missingSecurityAlert-missing required pkg claim
Package name does not matchSecurityAlert-package name does not match
v-state value is invalidSecurityAlert-invalid v-state
b_state claim missing while enforcing b_stateSecurityAlert-b_state is missing
TAB Validator initialization failedInternalError-failed to initialize validation
b_state verification failedSecurityAlert-b_state verification failed
Token claims are invalidINVALID-invalid token claims
Session duration not providedMISSING-session duration seconds is missing
Certificate serial Missing (when b_state is enforced and v_state is not unknown)MISSING-certificate serials not found
All Checks PassEmpty String

Implementation in Golang

Required Dependencies

Add the following to your go.mod:

require (
github.com/golang-jwt/jwt/v5 v5.3.1
google.golang.org/grpc v1.82.1
google.golang.org/protobuf v1.36.10
)

Implementation Example

package main

import (
"log"
"strings"

"MASSTTabService/TokenValidator"
"MASSTTabService/generated/TABServices"
)

func main() {
// =========================================================================
// STEP 1: Initialize the TAB Validator Module
// =========================================================================
validator, err := TokenValidator.InitValidator(
"./assets/rootCA.pem", // Bugsmirror Root CA PEM file path
"./assets/serialId.json", // Revoked hardware serial IDs JSON file path
"./tab-binaries/tab-validator-darwin-arm64", // TAB binary executable file path
8080, // Internal hosting port for TAB sidecar binary
)
if err != nil {
log.Fatalf("Fatal: Failed to initialize TAB validator module: %v", err)
return
}

// =========================================================================
// STEP 2: Extract Incoming Token from Request Header
// Retrieve the JWT string from client request header (e.g. x-ztrb-token)
// =========================================================================
tokenString := "eyJhbGciOiJSUzI1Ni..." // Replace with token fetched from client request header

// =========================================================================
// STEP 3: Perform TAB Token Validation
// Pass request token along with per-request security policies and parameters.
// =========================================================================

tabResp, tabErr := validator.Validate(
tokenString, // token fetched from client
"com.example.pkg", // package name of the application
60, // token expiration time
5, // clock drift duration
true, // enforces binding state
true, // enforces device leaf certificate expiry
int64(TokenValidator.ANDROID), // application OS
300, // session duration
[]string{""}..., // app identifiers
)


// 1. Check for TAB Errors
if tabErr != nil && tabErr.Msg != "" {
errMsg := tabErr.Msg

//"SecurityAlert-b_state is missing"
if strings.HasPrefix(errMsg, "SecurityAlert-b_state is missing") {
// If attestation b_state is missing and v_state is Unknown,
// the client environment cannot be established yet. Issue a Retry challenge to client.
if tabResp != nil && strings.EqualFold(tabResp.VState, "Unknown") {
log.Printf("[ACTION: RETRY] b_state is missing and v_state is UNKNOWN. Prompting client to re-attest and retry.")
// Response to client: Ask client app to re-generate attestation token and retry
return
}
//If v_state is not Unknown, missing b_state indicates tampered payload.
log.Printf("[ACTION: REJECT] b_state is missing and v_state is not Unknown. Rejecting request.")
// Response to client: HTTP 401 Unauthorized / Reject
return
}

// "SecurityAlert-b_state verification failed"
//Hardware attestation/signature check failed (tampered app/device).
if strings.HasPrefix(errMsg, "SecurityAlert-b_state verification failed") {
log.Printf("[ACTION: REJECT] b_state verification failed. Rejecting request.")
// Response to client: HTTP 401 Unauthorized / Reject
return
}

// Other Errors (Refer to Error Specifications Table)
log.Printf("[ACTION: REJECT] TAB Error encountered (%s). Rejecting request.", errMsg)
// Response to client: HTTP 401 Unauthorized / Reject
return
}

// 2. No TAB Error -> Check isRevoked Status & Certificate Expiry
if tabResp != nil {
// Hardware device ID is present in serialId.json blacklist. ALWAYS reject requests.
if tabResp.IsRevoked {
log.Printf("[ACTION: REJECT] Request rejected! Device hardware ID is REVOKED (Reason Code: %d).", tabResp.Res)
// Response to client: HTTP 403 Forbidden / Reject
return
}

// Can Be Enforced based on Business Requirement:
// Evaluates if any device attestation certificate in the chain has expired.
if tabResp.IsCertExpired {
log.Printf("[SECURITY WARNING] Device attestation certificate is EXPIRED (IsCertExpired = true).")
}

// 3. Is Nonce Validated?
// Validate that the one-time nonce has not been previously consumed in server session cache.
isNonceValid := validateNonce(tabResp.Nonce)//user Defined Function
if !isNonceValid {
log.Printf("[ACTION: REJECT] Nonce validation failed (replay attempt or invalid nonce). Rejecting request.")
// Response to client: HTTP 401 Unauthorized / Reject
return
}

// 4. All Security & Policy Checks Passed -> Allow User
log.Printf("[ACTION: ALLOW USER] TAB 2.0 Validation Successful!")
log.Printf(" • Nonce : %s", tabResp.Nonce)
log.Printf(" • Package Name : %s", tabResp.Pkg)
log.Printf(" • Security State : %s", tabResp.VState)
log.Printf(" • Cert Expired : %t", tabResp.IsCertExpired)
log.Printf(" • Issued At (iat): %d", tabResp.IssuedAt)
log.Printf(" • Clock Drift : %d seconds", tabResp.ClockValue)

// Proceed with backend business logic for authorized user request...
}

//This step is conditional: initiate this reload only upon receiving an email update of serial ID data.
if err := validator.ReloadSerialIds(); err != nil {
log.Printf("Failed to reload serial IDs: %v", err)
}
}

Error Specification

ConditionReturned Error (Message)
HTTP method not allowed“method not allowed”
Token string is missing from requestmissing token string
Request body is empty or malformedINVALID-Empty or invalid request body
Package name parameter missingMISSING-package name is missing
Expiry duration not providedMISSING-expiry duration seconds is missing
Clock drift seconds not providedMISSING-clock drift seconds is missing
App identifiers missingMISSING-missing application identifiers
Validator instance is nilMISSING-validator is nil
Required system config missingMISSING-system configuration Msg: missing required configuration parameters
Root CA PEM decode failedINVALID-system configuration Msg: failed to decode Root CA PEM
Root CA format invalidINVALID-system configuration Msg: invalid Root CA format
Token signature or chain validation failedMALFORMED-token signature/chain validation failed
'iat' (Issued At) claim missingSecurityAlert-missing 'iat' (Issued At) claim
Token timestamp is in the futureSecurityAlert-token claims to be from the future
Token has expiredUNAUTHORIZED-token expired
'nonce' claim missingSecurityAlert-missing 'nonce' claim
'v_state' claim missingSecurityAlert-missing required v_state claim
'pkg' claim missingSecurityAlert-missing required pkg claim
Package name does not matchSecurityAlert-package name does not match
v-state value is invalidSecurityAlert-invalid v-state
b_state claim missing while enforcing b_stateSecurityAlert-b_state is missing
TAB Validator initialization failedInternalError-failed to initialize validation
b_state verification failedSecurityAlert-b_state verification failed
Token claims are invalidINVALID-invalid token claims
Session duration not providedMISSING-session duration seconds is missing
Certificate serial Missing (when b_state is enforced and v_state is not unknown)MISSING-certificate serials not found
All Checks PassEmpty String

Impelementation in NodeJS

Required Dependencies

Add the following to your package.json:

"dependencies": {
"jsonwebtoken": "^9.0.2"
}

Implementation Example

const { Validator } = require('./validator');

function main() {
try {
const validator = new Validator(
"com.example.pkg", // package name of the application
300, // token expiration time
5, // clock drift duration
"./assets/rootCA.pem", // root certificate retrieved from the portal and
store into server's in-memory
"./assets/serialId.json" // path to serial ids JSON file
);
const tokenString = ""; // token is stored in request header
const result = validator.validate(tokenString);
// the values like clock value, package name, nonce, etc.
// can be retrieved from tab response even in case of error
if (result.tabError.error !== "") {
throw new Error(`Error occurred: ${result.tabError.error} with status:
${result.tabError.errorStatus}`);
}
if (result.tabResponse) {
// validate the revocation of the device from which the request is being
initiated
console.log(`Is Revoked: ${result.tabResponse.isRevoked}`);
// values extracted from claims
console.log(`Nonce: ${result.tabResponse.nonce}`);
console.log(`PackageName: ${result.tabResponse.packageName}`);
console.log(`Issued AT: ${result.tabResponse.issuedAt}`);
// duration in seconds calculated using issued at time
console.log(`Clock Value: ${result.tabResponse.clockValueInSeconds}`);
}
} catch (e) {
console.log(`Exception occurred: ${e.message}`);
}
}

main();

Error Sepcification

ConditionReturned ErrorStatusReturned Error (Message)
Validator config is missing/invalidMISSING"system configuration error: missing required configuration parameters"
tokenString is emptyMISSING"system configuration error: missing required configuration parameters" (Note: This reflects the exact code provided)
Token is not RSA signedMALFORMED"token signature/chain validation failed: unexpected signing algorithm: [alg]"
Missing/invalid x5c headerMALFORMED"token signature/chain validation failed: missing x5c certificate chain"
x5c leaf fails root verificationMALFORMED"token signature/chain validation failed: UNAUTHORIZED: Untrusted leaf certificate chain"
Missing iat claimSecurityAlert"missing 'iat' (Issued At) claim"
iat is further in future than allowed driftSecurityAlert"token claims to be from the future"
Token age exceeds expiryDurationUNAUTHORIZED"token expired"
Missing nonce claimSecurityAlert"missing 'nonce' claim"
Missing v_state claimSecurityAlert"missing required v_state claim"
Missing or mismatched pkg claimSecurityAlert"missing required pkg claim" OR "package name does not match"
All checks passSUCCESS(Empty String)