نصب‌های درون‌خطی گوگل پلی (SDK)

این صفحه توضیح می‌دهد که چگونه SDK های شخص ثالث می‌توانند نصب درون خطی را ادغام کنند، یک ویژگی آزمایشی جدید برای Google Play که جزئیات محصول برنامه Google Play را در یک رابط کاربری نیم صفحه‌ای ارائه می‌دهد. نصب درون خطی به کاربران این امکان را می‌دهد که بدون ترک محیط برنامه، یک جریان نصب یکپارچه برنامه را تجربه کنند.

توسعه‌دهندگان SDK شخص ثالث می‌توانند ویژگی نصب درون‌خطی را در SDKهای خود ادغام کنند تا توسعه‌دهندگان برنامه‌هایی که از آن SDKها استفاده می‌کنند، بتوانند به نصب‌های درون‌خطی برای برنامه‌های خود دسترسی داشته باشند.

الزامات

برای نمایش رابط نصب نیم صفحه‌ای درون‌خطی در یک برنامه:

  • حداقل نسخه گوگل پلی باید ۴۰.۴ باشد.
  • سطح API اندروید باید ۲۳ یا بالاتر باشد.

معماری فرآیند

معماری فرآیند نصب درون‌خطی در شکل زیر نشان داده شده است:

شکل ۱: نمای کلی معماری فرآیند نصب درون‌خطی.
  1. سرورهای گوگل پلی کلیدهای رمزگذاری Authenticated Encryption with Associated Data (AEAD) را تولید می‌کنند و کلیدها را در یک نمونه‌ی Google Cloud Platform (GCP) Secret Manager دریافت می‌کنند.
  2. یکپارچه‌ساز شخص ثالث، کلید AEAD را از GCP Secret Manager بازیابی می‌کند.
  3. یکپارچه‌ساز شخص ثالث، داده‌های Intent نصب درون‌خطی را رمزگذاری می‌کند، متن رمزی ارسال شده در لینک عمیق مورد استفاده برای فراخوانی قصد نصب درون‌خطی را تولید می‌کند و لینک‌های عمیق را در پاسخ‌ها به کلاینت ارسال می‌کند.
  4. وقتی لینک عمیق دنبال می‌شود، برنامه گوگل پلی اینتنت را مدیریت می‌کند.

برای پیکربندی یک SDK شخص ثالث برای استفاده از فرآیند نصب درون‌خطی، مراحل زیر را انجام دهید.

ایجاد حساب‌های کاربری سرویس در پروژه ابری گوگل

در این مرحله، شما با استفاده از کنسول ابری گوگل، یک حساب کاربری سرویس ایجاد می‌کنید.

  1. یک پروژه ابری گوگل راه‌اندازی کنید:
    • یک سازمان Google Cloud ایجاد کنید. وقتی یک حساب Google Workspace یا Cloud Identity ایجاد می‌کنید و آن را با نام دامنه خود مرتبط می‌کنید، منبع سازمان به طور خودکار ایجاد می‌شود. برای جزئیات بیشتر، به ایجاد و مدیریت منابع سازمان مراجعه کنید.
    • با استفاده از حساب Google Cloud که در مرحله قبل ایجاد کرده‌اید، وارد کنسول GCP شوید، سپس یک پروژه Google Cloud ایجاد کنید. برای جزئیات بیشتر، به «ایجاد یک پروژه Google Cloud» مراجعه کنید.
  2. یک حساب کاربری سرویس در پروژه Google Cloud ایجاد شده ایجاد کنید. این حساب کاربری سرویس به عنوان یک هویت Google Cloud برای دسترسی به کلید متقارن از طرف سرورهای شما استفاده می‌شود. برای جزئیات بیشتر، به «ایجاد یک حساب کاربری سرویس» مراجعه کنید.
  3. از همان شناسه مشتری Google Workspace (GWCID) / شناسه Dasher که در فرم علاقه‌مندی وارد کرده‌اید، استفاده کنید.
  4. کلید خصوصی آن حساب سرویس را ایجاد و دانلود کنید.
  5. برای آن حساب سرویس، یک کلید ایجاد کنید. برای جزئیات بیشتر، به «ایجاد کلید حساب سرویس» مراجعه کنید.
  6. کلید حساب سرویس را دانلود کنید و آن را روی سرور خود در دسترس نگه دارید، زیرا برای احراز هویت جهت دسترسی به منابع Google Cloud برای کلیدهای متقارن استفاده می‌شود. برای جزئیات بیشتر، به دریافت کلید حساب سرویس مراجعه کنید.

بازیابی اعتبارنامه‌ها

در این مرحله، شما کلید متقارن را از Secret Manager بازیابی کرده و آن را به صورت ایمن (مثلاً در یک فایل JSON) در فضای ذخیره‌سازی سرور خود ذخیره می‌کنید. این کلید برای تولید متن رمزگذاری شده‌ی درون‌خطی داده‌های نصب استفاده می‌شود.

مقادیر secret_id/secretId به نام مخفی درون Secret Manager اشاره دارند؛ این نام با اضافه کردن hsdp-3p-key- به مقدار sdk_id که توسط Play ارائه شده است، ایجاد می‌شود. برای مثال، اگر sdk_id برابر با abc باشد، نام مخفی hsdp-3p-key-abc خواهد بود.

نسخه‌های مخفی هر هفته سه‌شنبه‌ها ساعت ۲ بعد از ظهر به وقت جهانی به‌روزرسانی می‌شوند. دومین کلیدهای جدید تا چرخش بعدی به کار خود ادامه می‌دهند و اطلاعات مربوط به کلید باید به صورت هفتگی و تازه دریافت و ذخیره شوند.

مثال پایتون

مثال کد زیر از یک توکن دسترسی ذخیره شده در یک فایل JSON برای دسترسی به محتوای کلید در GCP Secret Manager و چاپ آن در کنسول استفاده می‌کند.

#!/usr/bin/env python3
# Import the Secret Manager client library.
from google.cloud import secretmanager
from google.oauth2 import service_account
import google_crc32c

# Create a service account key file.
service_account_key_file = "<json key file of the service account>"
credentials = service_account.Credentials.from_service_account_file(service_account_key_file)

# Create the Secret Manager client.
client = secretmanager.SecretManagerServiceClient(
  credentials=credentials
)

# Build the resource name of the secret version.
name = f"projects/prod-play-hsdp-3p-caller-auth/secrets/<secret_id>/versions/latest"

# Access the secret version.
response = client.access_secret_version(request={"name": name})

# Verify payload checksum.
crc32c = google_crc32c.Checksum()
crc32c.update(response.payload.data)
if response.payload.data_crc32c != int(crc32c.hexdigest(), 16):
    print("Data corruption detected.")

# A keyset created with "tinkey create-keyset --key-template=AES256_GCM". Note
# that this keyset has the secret key information in cleartext.
keyset = response.payload.data.decode("UTF-8")

# WARNING: Do not print the secret in a production environment. Please store it
# in a secure storage.
with open('<key file name>', 'w') as f:
    f.write(keyset)

مثال جاوا

مثال کد زیر از یک توکن دسترسی ذخیره شده در یک فایل JSON برای دسترسی به محتوای کلید در GCP Secret Manager و نوشتن آن در یک فایل JSON استفاده می‌کند.

import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.api.gax.core.CredentialsProvider;
import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.secretmanager.v1.AccessSecretVersionResponse;
import com.google.cloud.secretmanager.v1.SecretManagerServiceClient;
import com.google.cloud.secretmanager.v1.SecretManagerServiceSettings;
import com.google.cloud.secretmanager.v1.SecretVersionName;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.zip.CRC32C;
import java.util.zip.Checksum;

/** */
final class ThirdPartySecretAccessGuide {

  private ThirdPartySecretAccessGuide() {}

  public static void main(String[] args) throws IOException {
    accessSecretVersion();
  }

  public static void accessSecretVersion() throws IOException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "projectId";
    String secretId = "secretId";
    String versionId = "versionId";
    String accessTokenPrivateKeyPath = "path/to/credentials.json";
    String secretMaterialOutputPath = "path/to/secret.json";
    accessSecretVersion(
        projectId, secretId, versionId, accessTokenPrivateKeyPath, secretMaterialOutputPath);
  }

  // Access the payload for the given secret version if one exists. The version
  // can be a version number as a string (e.g. "5") or an alias (e.g. "latest").
  public static void accessSecretVersion(
      String projectId,
      String secretId,
      String versionId,
      String accessTokenPrivateKeyPath,
      String secretMaterialOutputPath)
      throws IOException {

    // We can explicitly instantiate the SecretManagerServiceClient (below) from a json file if we:
    // 1. Create a CredentialsProvider from a FileInputStream of the JSON file,
    CredentialsProvider credentialsProvider =
        FixedCredentialsProvider.create(
            ServiceAccountCredentials.fromStream(new FileInputStream(accessTokenPrivateKeyPath)));

    // 2. Build a SecretManagerService Settings object from that credentials provider, and
    SecretManagerServiceSettings secretManagerServiceSettings =
        SecretManagerServiceSettings.newBuilder()
            .setCredentialsProvider(credentialsProvider)
            .build();

    // 3. Initialize client that will be used to send requests by passing the settings object to
    // create(). This client only needs to be created once, and can be reused for multiple requests.
    // After completing all of your requests, call the "close" method on the client to safely clean
    // up any remaining background resources.
    try (SecretManagerServiceClient client =
        SecretManagerServiceClient.create(secretManagerServiceSettings)) {
      SecretVersionName secretVersionName = SecretVersionName.of(projectId, secretId, versionId);

      // Access the secret version.
      AccessSecretVersionResponse response = client.accessSecretVersion(secretVersionName);

      // Verify checksum. The used library is available in Java 9+.
      // If using Java 8, you may use the following:
      // https://github.com/google/guava/blob/e62d6a0456420d295089a9c319b7593a3eae4a83/guava/src/com/google/common/hash/Hashing.java#L395
      byte[] data = response.getPayload().getData().toByteArray();
      Checksum checksum = new CRC32C();
      checksum.update(data, 0, data.length);
      if (response.getPayload().getDataCrc32C() != checksum.getValue()) {
        System.out.printf("Data corruption detected.");
        return;
      }

      String payload = response.getPayload().getData().toStringUtf8();
      // Print the secret payload.
      //
      // WARNING: Do not print the secret in a production environment - this
      // snippet is showing how to access the secret material.
      System.out.printf("Plaintext: %s\n", payload);

      // Write the JSON secret material payload to a json file
      try (PrintWriter out =
          new PrintWriter(Files.newBufferedWriter(Paths.get(secretMaterialOutputPath), UTF_8))) {
        out.write(payload);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
}

تنظیم اعتبارنامه‌های پیش‌فرض برنامه

اگر نمی‌خواهید از CredentialsProvider برای ارسال کلید خصوصی به یک فایل JSON در پیاده‌سازی جاوا استفاده کنید، می‌توانید پیاده‌سازی را با تنظیم Application Default Credentials (ADC) تغییر دهید:

  1. به کتابخانه‌های کلاینت بگویید که کلید حساب سرویس را کجا پیدا کنند.
  2. وابستگی‌های Maven را به پروژه جاوا اضافه کنید .
  3. تابع SecretManagerServiceClient.create() را فراخوانی کنید ، که به دلیل مرحله ۱، احراز هویت را به طور خودکار انجام می‌دهد.

این مراحل، پیاده‌سازی جاوا را به صورت زیر تغییر می‌دهند:

  • حذف نیاز به ایجاد اشیاء CredentialsProvider و SecretManagerServiceSettings .
  • تغییر فراخوانی به SecretManagerServiceClient.create() برای شامل نشدن هیچ آرگومانی.

ایجاد متن رمزی و ایجاد پیوند عمیق

در این مرحله، شما از کتابخانه رمزنگاری Tink برای ایجاد enifd (متن رمزنگاری InlineInstallData ) از شیء InlineInstallData protobuf استفاده می‌کنید. پروتو InlineInstallData به صورت زیر تعریف می‌شود:

syntax = "proto2";
package hsdpexperiments;
option java_package = "com.google.hsdpexperiments";
option java_multiple_files = true;

// InlineInstallData is used by 3p auth callers to generate "encrypted inline
// flow data" (enifd) which is decrypted in PGS to verify authenticity and
// freshness.
message InlineInstallData {
  // The timestamp which indicates the time encrypted data is generated.
  // Used to validate freshness (i.e. generation time in past 4 hours).
  // Required.
  optional int64 timestamp_ms = 1;

  // The docid of the app that we want to open inline install page for.
  // This is the package name.
  // Required.
  optional string target_package_name = 2;

  // This is the name of the app requesting the ad from Google Ad Serving
  // system.
  // Required.
  optional string caller_package_name = 3;

  // This is the advertising id that will be collected by 3P Ad SDKs.
  // Optional.
  optional string advertising_id = 4;

  // This is used to indicate the network from where the inline install was
  // requested.
  // Required.
  optional string ad_network_id = 5;
}

در این مرحله شما همچنین با استفاده از این پارامترها، آدرس اینترنتی لینک عمیق (deep link URL) را می‌سازید:

فیلدها توضیحات مورد نیاز
شناسه نام بسته برنامه‌ای که قرار است نصب شود. بله
درون خطی اگر نصب درون‌خطی نیم‌صفحه درخواست شود، روی true تنظیم می‌شود؛ اگر false ، intent deep به Google Play لینک می‌دهد. بله
انیفد شناسه رمزگذاری شده برای SDK های 3P. بله
لفت یک شناسه داخلی. بله
شناسه‌ی احراز هویت 3pAuthCaller شناسه SDK. بله
فهرست بندی یک پارامتر اختیاری برای تعیین هدف برای یک فهرست فروشگاه سفارشی . خیر
ارجاع دهنده یک رشته ردیابی ارجاع‌دهنده اختیاری. خیر

مثال پایتون

دستور زیر کد پایتون را از InlineInstallData.proto تولید می‌کند:

protoc InlineInstallData.proto --python_out=.

کد نمونه پایتون زیر، InlineInstallData می‌سازد و آن را با کلید متقارن رمزگذاری می‌کند تا متن رمز شده ایجاد شود:

#!/usr/bin/env python3

# Import the Secret Manager client library.
import base64
import time
import inline_install_data_pb2 as InlineInstallData
import tink
from tink import aead
from tink import cleartext_keyset_handle

# Read the stored symmetric key.
with open("example3psecret.json", "r") as f:
  keyset = f.read()

"""Encrypt and decrypt using AEAD."""
# Register the AEAD key managers. This is needed to create an Aead primitive later.
aead.register()

# Create a keyset handle from the cleartext keyset in the previous
# step. The keyset handle provides abstract access to the underlying keyset to
# limit access of the raw key material. WARNING: In practice, it is unlikely
# you will want to use a cleartext_keyset_handle, as it implies that your key
# material is passed in cleartext, which is a security risk.
keyset_handle = cleartext_keyset_handle.read(tink.JsonKeysetReader(keyset))

# Retrieve the Aead primitive we want to use from the keyset handle.
primitive = keyset_handle.primitive(aead.Aead)

inlineInstallData = InlineInstallData.InlineInstallData()
inlineInstallData.timestamp_ms = int(time.time() * 1000)
inlineInstallData.target_package_name = "x.y.z"
inlineInstallData.caller_package_name = "a.b.c"
inlineInstallData.ad_network_id = "<sdk_id>"

# Use the primitive to encrypt a message. In this case the primary key of the
# keyset will be used (which is also the only key in this example).
ciphertext = primitive.encrypt(inlineInstallData.SerializeToString(), b'<sdk_id>')
print(f"InlineInstallData Ciphertext: {ciphertext}")

# Base64 Encoded InlineInstallData Ciphertext
enifd = base64.urlsafe_b64encode(ciphertext).decode('utf-8')
print(enifd)

# Deeplink
print(f"https://play.google.com/d?id={inlineInstallData.target_package_name}\&inline=true\&enifd={enifd}\&lft=1\&3pAuthCallerId={inlineInstallData.ad_network_id}")

با اجرای دستور زیر، اسکریپت پایتون را اجرا کنید:

python <file_name>.py

مثال جاوا

دستور زیر کد جاوا را از InlineInstallData.proto تولید می‌کند:

protoc InlineInstallData.proto --java_out=.

کد نمونه جاوای زیر InlineInstallData می‌سازد و آن را با کلید متقارن رمزگذاری می‌کند تا متن رمز شده ایجاد شود:

package com.google.hsdpexperiments;

import static com.google.common.io.BaseEncoding.base64Url;
import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.flags.Flag;
import com.google.common.flags.FlagSpec;
import com.google.common.flags.Flags;
import com.google.crypto.tink.Aead;
import com.google.crypto.tink.InsecureSecretKeyAccess;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.TinkJsonProtoKeysetFormat;
import com.google.crypto.tink.aead.AeadConfig;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.Security;
import java.time.Duration;
import org.conscrypt.Conscrypt;

/** info on encryption in https://github.com/google/tink#learn-more */
final class ThirdPartyEnifdGuide {

  @FlagSpec(
      name = "third_party_id",
      help = "the identifier associated with the 3p for which to generate the enifd")
  private static final Flag<String> thirdPartyAuthCallerId = Flag.value("");

  @FlagSpec(name = "package_name", help = "the package name of the target app")
  private static final Flag<String> packageName = Flag.value("");


  @FlagSpec(name = "caller_package_name", help = "the package name of the caller app")
  private static final Flag<String> callerPackageName = Flag.value("");

  @FlagSpec(name = "secret_filename", help = "the path to the json file with the secret material")
  private static final Flag<String> secretFilename = Flag.value("");

  private ThirdPartyEnifdGuide() {}

  public static void main(String[] args) throws Exception {
    // parse flags
    Flags.parse(args);

    // File keyFile = new File(args[0]);
    Path keyFile = Paths.get(secretFilename.get());

    // Create structured inline flow data
    InlineInstallData idrp =
        InlineInstallData.newBuilder()
            .setTargetPackageName(packageName.get())
            .setCallerPackageName(callerPackageName.get())
            .setTimestampMs(System.currentTimeMillis())
            .setAdNetworkId(thirdPartyAuthCallerId.get())
            .build();

    // we can print this out here to make sure it's well formatted, this will help debug
    System.out.println(idrp.toString());

    // Register all AEAD key types with the Tink runtime.
    Conscrypt.checkAvailability();
    Security.addProvider(Conscrypt.newProvider());
    AeadConfig.register();

    // Read AEAD key downloaded from secretmanager into keysethandle
    KeysetHandle handle =
        TinkJsonProtoKeysetFormat.parseKeyset(
            new String(Files.readAllBytes(keyFile), UTF_8), InsecureSecretKeyAccess.get());

    // Generate enifd using tink library
    Aead aead = handle.getPrimitive(Aead.class);
    byte[] plaintext = idrp.toByteArray();
    byte[] ciphertext = aead.encrypt(plaintext, thirdPartyAuthCallerId.get().getBytes(UTF_8));
    String enifd = base64Url().omitPadding().encode(ciphertext);

    // Build deeplink, escaping ampersands (TODO: verify this is necessary while testing e2e)
    String deeplink =
        "https://play.google.com/d?id="
            + packageName.get()
            + "\\&inline=true\\&enifd="
            + enifd
            + "\\&lft=1\\&3pAuthCallerId="
            + thirdPartyAuthCallerId.get();

    System.out.println(deeplink);
  }
}

در نهایت، برنامه جاوا را به صورت دودویی بسازید و با استفاده از کد زیر آن را فراخوانی کنید:

path/to/binary/ThirdPartyEnifdGuide --secret_filename=path/to/jsonfile/example3psecret.json --package_name=<package_name_of_target_app> --third_party_id=<3p_caller_auth_id>
  • پرچم secret_filename مسیر فایل JSON حاوی محتوای مخفی را مشخص می‌کند.
  • پرچم package_name شناسه‌ی سند (doc ID) برنامه‌ی هدف است.
  • پرچم third_party_id برای مشخص کردن شناسه احراز هویت فراخواننده شخص ثالث (یعنی <sdk_id> ) استفاده می‌شود.

اجرای Inline Install intent

برای آزمایش پیوند عمیقی که در مرحله قبل ایجاد شده است، یک دستگاه اندروید (مطمئن شوید که اشکال‌زدایی USB فعال است) را به یک ایستگاه کاری که ADB روی آن نصب شده است متصل کنید و دستور زیر را اجرا کنید:

adb shell am start "<output_from_the_previous_python_or_java_code>"

در کد کلاینت، اینتنت را با استفاده از یکی از روش‌های زیر (کاتلین یا جاوا) ارسال کنید.

کاتلین

val intent = Intent(Intent.ACTION_VIEW)
val deepLinkUrl = "<output_from_the_previous_python_or_java_code>"
intent.setPackage("com.android.vending")
intent.data = Uri.parse(deepLinkUrl)
val packageManager = context.getPackageManager()
if (intent.resolveActivity(packageManager) != null) {
  startActivityForResult(intent, 0)
} else {
  // Fallback to deep linking to full Play Store.
}

جاوا

Intent intent = new Intent(Intent.ACTION_VIEW);
String id = "exampleAppToBeInstalledId";
String deepLinkUrl = "<output_from_the_previous_python_or_java_code>";
intent.setPackage("com.android.vending");
intent.setData(Uri.parse(deepLinkUrl));
PackageManager packageManager = context.getPackageManager();
if (intent.resolveActivity(packageManager) != null) {
  startActivityForResult(intent, 0);
} else {
  // Fallback to deep linking to full Play Store.
}

پیوست

بخش‌های بعدی راهنمایی‌های بیشتری در مورد موارد استفاده خاص ارائه می‌دهند.

آماده‌سازی محیط پایتون

برای اجرای کد نمونه پایتون، محیط پایتون را روی سیستم خود راه‌اندازی کنید و وابستگی‌های مورد نیاز را نصب کنید.

  1. محیط پایتون را تنظیم کنید:

    1. پایتون ۳.۱۱ را نصب کنید (اگر از قبل نصب شده است، از این مرحله صرف نظر کنید):

      sudo apt install python3.11
      
    2. پیپ را نصب کنید:

      sudo apt-get install pip
      
    3. نصب virtualenv :

      sudo apt install python3-virtualenv
      
    4. یک محیط مجازی ایجاد کنید (برای وابستگی به Tink لازم است):

      virtualenv inlineinstall --python=/usr/bin/python3.11
      
  2. وارد محیط مجازی شوید:

    source inlineinstall/bin/activate
    
  3. به‌روزرسانی پیپ:

    python -m pip install --upgrade pip
    
  4. وابستگی‌های مورد نیاز را نصب کنید:

    1. نصب تینک:

      pip install tink
      
    2. نصب گوگل crc32c:

      pip install google-crc32c
      
    3. نصب مدیر مخفی:

      pip install google-cloud-secret-manager
      
    4. کامپایلر protobuf را نصب کنید:

      sudo apt install protobuf-compiler
      

تولید enifd در ++C

در ادامه یک مثال ++C که خودمان نوشته‌ایم و به صورت داخلی اعتبارسنجی کرده‌ایم تا enifd را تولید کند، آورده شده است.

تولید enifd می‌تواند با استفاده از کد C++ به صورت زیر انجام شود:

// A command-line example for using Tink AEAD w/ key template aes128gcmsiv to
// encrypt an InlineInstallData proto.
#include <chrono>
#include <iostream>
#include <memory>
#include <string>

#include "<path_to_protoc_output>/inline_install_data.proto.h"
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "tink/cc/aead.h"
#include "tink/cc/aead_config.h"
#include "tink/cc/aead_key_templates.h"
#include "tink/cc/config/global_registry.h"
#include "tink/cc/examples/util/util.h"
#include "tink/cc/keyset_handle.h"
#include "tink/cc/util/status.h"
#include "tink/cc/util/statusor.h"

ABSL_FLAG(std::string, keyset_filename, "",
          "Keyset file (downloaded from secretmanager) in JSON format");
ABSL_FLAG(std::string, associated_data, "",
          "Associated data for AEAD (default: empty");

namespace {

using ::crypto::tink::Aead;
using ::crypto::tink::AeadConfig;
using ::crypto::tink::KeysetHandle;
using ::crypto::tink::util::Status;
using ::crypto::tink::util::StatusOr;

}  // namespace

namespace tink_cc_examples {

// AEAD example CLI implementation.
void AeadCli(const std::string& keyset_filename,
             absl::string_view associated_data) {
  Status result = AeadConfig::Register();
  if (!result.ok()) {
    std::clog << "Failed to register AeadConfig";
    return;
  }

  // Read the keyset from file.
  StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle =
      ReadJsonCleartextKeyset(keyset_filename);
  if (!keyset_handle.ok()) {
    std::clog << "Failed to read json keyset";
    return;
  }

  // Get the primitive.
  StatusOr<std::unique_ptr<Aead>> aead =
      (*keyset_handle)
          ->GetPrimitive<crypto::tink::Aead>(
              crypto::tink::ConfigGlobalRegistry());
  if (!aead.ok()) {
    std::clog << "Failed to get primitive";
    return;
  }

  // Instantiate the enifd.
  hsdpexperiments::InlineInstallData iid;

  iid.set_timestamp_ms(std::chrono::duration_cast<std::chrono::milliseconds>(
                           std::chrono::system_clock::now().time_since_epoch())
                           .count());
  iid.set_target_package_name("<TARGET_PACKAGE_NAME>");
  iid.set_caller_package_name("<CALLER_PACKAGE_NAME>");
  iid.set_ad_network_id("<SDK_ID>");

  // Compute the output.
  StatusOr<std::string> encrypt_result =
      (*aead)->Encrypt(iid.SerializeAsString(), associated_data);
  if (!encrypt_result.ok()) {
    std::clog << "Failed to encrypt Inline Install Data";
    return;
  }
  const std::string& output = encrypt_result.value();

  std::string enifd;
  absl::WebSafeBase64Escape(output, &enifd);

  std::clog << "enifd: " << enifd << '\n';
}

}  // namespace tink_cc_examples

int main(int argc, char** argv) {
  absl::ParseCommandLine(argc, argv);

  std::string keyset_filename = absl::GetFlag(FLAGS_keyset_filename);
  std::string associated_data = absl::GetFlag(FLAGS_associated_data);

  std::clog << "Using keyset from file " << keyset_filename
            << " to AEAD-encrypt inline install data with associated data '"
            << associated_data << "'." << '\n';

  tink_cc_examples::AeadCli(keyset_filename, associated_data);
  return 0;
}

این کد از نمونه‌ای اقتباس شده است که می‌توانید در مستندات Tink آن را پیدا کنید.