Giải mã giá trị nhận dạng nhà quảng cáo cho mạng quảng cáo

Mạng quảng cáo sử dụng thẻ JavaScript để lấp đầy quảng cáo thông qua Authorized Buyers sẽ đủ điều kiện nhận giá trị nhận dạng nhà quảng cáo cho cả thiết bị Android và iOS. Thông tin được gửi qua macro %%EXTRA_TAG_DATA%% hoặc %%ADVERTISING_IDENTIFIER%% trong thẻ JavaScript do Authorized Buyers quản lý. Phần còn lại của phần này tập trung vào việc trích xuất %%EXTRA_TAG_DATA%%, nhưng hãy xem bài viết Tái tiếp thị với IDFA hoặc Mã nhận dạng cho quảng cáo để biết thông tin chi tiết về vùng đệm proto đã mã hoá %%ADVERTISING_IDENTIFIER%% MobileAdvertisingId có thể được giải mã tương tự.

Lịch trình

  1. Mạng quảng cáo sẽ cập nhật các thẻ JavaScript trong ứng dụng thông qua giao diện người dùng của Authorized Buyers, thêm vào macro %%EXTRA_TAG_DATA%% như giải thích bên dưới.
  2. Tại thời điểm phân phát, ứng dụng sẽ yêu cầu quảng cáo từ Authorized Buyers thông qua SDK quảng cáo trên thiết bị di động của Google, đồng thời truyền giá trị nhận dạng nhà quảng cáo một cách an toàn.
  3. Ứng dụng sẽ nhận lại thẻ JavaScript, cùng với macro %%EXTRA_TAG_DATA%% được điền cùng với vùng đệm giao thức mạng quảng cáo đã mã hoá có chứa giá trị nhận dạng đó.
  4. Ứng dụng chạy thẻ này và thực hiện lệnh gọi đến Mạng quảng cáo để hiển thị quảng cáo giành chiến thắng.
  5. Để sử dụng (kiếm tiền) thông tin này, Mạng quảng cáo phải xử lý vùng đệm giao thức:
    1. Giải mã chuỗi websafe trở lại thành chuỗi byte bằng WebSafeBase64.
    2. Hãy giải mã mã đó bằng lược đồ nêu dưới đây.
    3. Giải tuần tự proto và lấy mã nhận dạng nhà quảng cáo từ ExtraTagData.advertising_id hoặc ExtraTagData.hashed_idfa.

Phần phụ thuộc

  1. Bộ mã hoá WebSafeBase64.
  2. Thư viện mật mã hỗ trợ HMAC SHA-1, chẳng hạn như OpenSSL.
  3. Trình biên dịch vùng đệm giao thức của Google.

Giải mã chuỗi websafe

Vì thông tin gửi qua macro %%EXTRA_TAG_DATA%% phải được gửi qua URL, nên các máy chủ của Google sẽ mã hoá thông tin đó bằng base64 an toàn trên web (RFC 3548).

Do đó, trước khi thử giải mã, bạn phải giải mã các ký tự ASCII trở lại thành chuỗi byte. Mã C++ mẫu bên dưới dựa trên BIO_f_base64() của Dự án OpenSSL và là một phần của mã giải mã mẫu của Google.

string AddPadding(const string& b64_string) {
  if (b64_string.size() % 4 == 3) {
    return b64_string + "=";
  } else if (b64_string.size() % 4 == 2) {
    return b64_string + "==";
  }
  return b64_string;
}

// Adapted from http://www.openssl.org/docs/man1.1.0/crypto/BIO_f_base64.html
// Takes a web safe base64 encoded string (RFC 3548) and decodes it.
// Normally, web safe base64 strings have padding '=' replaced with '.',
// but we will not pad the ciphertext. We add padding here because
// openssl has trouble with unpadded strings.
string B64Decode(const string& encoded) {
  string padded = AddPadding(encoded);
  // convert from web safe -> normal base64.
  int32 index = -1;
  while ((index = padded.find_first_of('-', index + 1)) != string::npos) {
    padded[index] = '+';
  }
  index = -1;
  while ((index = padded.find_first_of('_', index + 1)) != string::npos) {
    padded[index] = '/';
  }

  // base64 decode using openssl library.
  const int32 kOutputBufferSize = 256;
  char output[kOutputBufferSize];

  BIO* b64 = BIO_new(BIO_f_base64());
  BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
  BIO* bio = BIO_new_mem_buf(const_cast(padded.data()),
                             padded.length());
  bio = BIO_push(b64, bio);
  int32 out_length = BIO_read(bio, output, kOutputBufferSize);
  BIO_free_all(bio);
  return string(output, out_length);
}

Cấu trúc của chuỗi byte được mã hoá

Sau khi giải mã các ký tự ASCII trở lại thành một chuỗi byte, bạn đã sẵn sàng giải mã chuỗi đó. Chuỗi byte được mã hoá chứa 3 phần:

  • initialization_vector: 16 byte.
  • ciphertext: chuỗi các phần 20 byte.
  • integrity_signature: 4 byte.
{initialization_vector (16 bytes)}{ciphertext (20-byte sections)}{integrity_signature (4 bytes)}

Mảng byte ciphertext được chia thành nhiều phần có kích thước 20 byte, ngoại trừ phần cuối cùng có thể chứa từ 1 đến 20 byte. Đối với mỗi phần của byte_array gốc, ciphertext 20 byte tương ứng sẽ được tạo như sau:

<byte_array <xor> HMAC(encryption_key, initialization_vector || counter_bytes)>

nơi nối ||.

Định nghĩa

Biến Thông tin chi tiết
initialization_vector 16 byte – duy nhất cho lượt hiển thị.
encryption_key 32 byte – được cung cấp khi thiết lập tài khoản.
integrity_key 32 byte – được cung cấp khi thiết lập tài khoản.
byte_array Đối tượng ExtraTagData được tuần tự hoá, trong các phần 20 byte.
counter_bytes Giá trị byte thể hiện số thứ tự của phần, hãy xem bên dưới.
final_message Tổng mảng byte được gửi qua macro %%EXTRA_TAG_DATA%% (trừ đi phương thức mã hoá WebSafeBase64).
Toán tử Thông tin chi tiết
hmac(key, data) SHA-1 HMAC, sử dụng key để mã hoá data.
a || b chuỗi a nối với chuỗi b.

Tính bộ đếm_byte

counter_bytes đánh dấu thứ tự của từng phần 20 byte của ciphertext. Xin lưu ý rằng phần cuối cùng có thể chứa từ 1 đến 20 byte. Để điền giá trị chính xác vào counter_bytes khi chạy hàm hmac(), hãy đếm các phần 20 byte (bao gồm cả phần còn lại) và sử dụng bảng tham chiếu sau:

Số mục Giá trị counter_bytes
0 Không có
1 ... 256 1 byte. Giá trị tăng từ 0 đến 255 liên tiếp.
257 ... 512 2 byte. Giá trị của byte đầu tiên là 0, giá trị của byte thứ hai tăng dần từ 0 lên 255.
513 ... 768 3 byte. Giá trị của hai byte đầu tiên là 0, giá trị của byte cuối cùng tăng dần từ 0 lên 255.

Trở lại đầu trang

Lược đồ mã hoá

Lược đồ mã hoá dựa trên cùng một lược đồ dùng để giải mã tín hiệu nhắm mục tiêu siêu cục bộ.

  1. Chuyển đổi tuần tự: Một thực thể của đối tượng ExtraTagData như đã xác định trong vùng đệm giao thức được chuyển đổi tuần tự lần đầu thông qua SerializeAsString() thành một mảng byte.

  2. Mã hoá: Sau đó, mảng byte được mã hoá bằng một lược đồ mã hoá tuỳ chỉnh được thiết kế để giảm thiểu hao tổn về kích thước trong khi vẫn đảm bảo khả năng bảo mật đầy đủ. Lược đồ mã hoá sử dụng một thuật toán HMAC có khoá để tạo vùng đệm bí mật dựa trên initialization_vector dành riêng cho sự kiện hiển thị.

Mã giả để mã hoá

byte_array = SerializeAsString(ExtraTagData object)
pad = hmac(encryption_key, initialization_vector ||
      counter_bytes )  // for each 20-byte section of byte_array
ciphertext = pad <xor> byte_array // for each 20-byte section of byte_array
integrity_signature = hmac(integrity_key, byte_array ||
                      initialization_vector)  // first 4 bytes
final_message = initialization_vector || ciphertext || integrity_signature

Lược đồ giải mã

Mã giải mã của bạn phải 1) giải mã vùng đệm giao thức bằng khoá mã hoá và 2) xác minh bit tính toàn vẹn bằng khoá tính toàn vẹn. Khoá sẽ được cung cấp cho bạn trong quá trình thiết lập tài khoản. Không có hạn chế nào về cách bạn cấu trúc cấu trúc triển khai. Trong hầu hết trường hợp, bạn sẽ có thể lấy mã mẫu và điều chỉnh mã theo nhu cầu của mình.

  1. Tạo bảng tính: HMAC(encryption_key, initialization_vector || counter_bytes)
  2. XOR: Lấy kết quả này và <xor> cùng với bản mật mã để đảo ngược quá trình mã hoá.
  3. Xác minh: Chữ ký về tính toàn vẹn truyền 4 byte HMAC(integrity_key, byte_array || initialization_vector)

Mã giả giải mã

// split up according to length rules
(initialization_vector, ciphertext, integrity_signature) = final_message

// for each 20-byte section of ciphertext
pad = hmac(encryption_key, initialization_vector || counter_bytes)

// for each 20-byte section of ciphertext
byte_array = ciphertext <xor> pad

confirmation_signature = hmac(integrity_key, byte_array ||
                         initialization_vector)
success = (confirmation_signature == integrity_signature)

Mã C++ mẫu

Ở đây là một hàm khoá từ mã ví dụ về giải mã hoàn chỉnh của chúng tôi.

bool DecryptByteArray(
    const string& ciphertext, const string& encryption_key,
    const string& integrity_key, string* cleartext) {
  // Step 1. find the length of initialization vector and clear text.
  const int cleartext_length =
     ciphertext.size() - kInitializationVectorSize - kSignatureSize;
  if (cleartext_length < 0) {
    // The length cannot be correct.
    return false;
  }

  string iv(ciphertext, 0, kInitializationVectorSize);

  // Step 2. recover clear text
  cleartext->resize(cleartext_length, '\0');
  const char* ciphertext_begin = string_as_array(ciphertext) + iv.size();
  const char* const ciphertext_end = ciphertext_begin + cleartext->size();
  string::iterator cleartext_begin = cleartext->begin();

  bool add_iv_counter_byte = true;
  while (ciphertext_begin < ciphertext_end) {
    uint32 pad_size = kHashOutputSize;
    uchar encryption_pad[kHashOutputSize];

    if (!HMAC(EVP_sha1(), string_as_array(encryption_key),
              encryption_key.length(), (uchar*)string_as_array(iv),
              iv.size(), encryption_pad, &pad_size)) {
      printf("Error: encryption HMAC failed.\n");
      return false;
    }

    for (int i = 0;
         i < kBlockSize && ciphertext_begin < ciphertext_end;
         ++i, ++cleartext_begin, ++ciphertext_begin) {
      *cleartext_begin = *ciphertext_begin ^ encryption_pad[i];
    }

    if (!add_iv_counter_byte) {
      char& last_byte = *iv.rbegin();
      ++last_byte;
      if (last_byte == '\0') {
        add_iv_counter_byte = true;
      }
    }

    if (add_iv_counter_byte) {
      add_iv_counter_byte = false;
      iv.push_back('\0');
    }
  }

Lấy dữ liệu từ vùng đệm giao thức mạng quảng cáo

Sau khi giải mã và giải mã dữ liệu được truyền vào %%EXTRA_TAG_DATA%%, bạn đã sẵn sàng giải trình tự vùng đệm giao thức và nhận giá trị nhận dạng nhà quảng cáo để nhắm mục tiêu.

Nếu bạn chưa hiểu rõ về vùng đệm giao thức, hãy bắt đầu với tài liệu của chúng tôi.

Định nghĩa

Vùng đệm giao thức mạng quảng cáo của chúng tôi được xác định như sau:

message ExtraTagData {
  // advertising_id can be Apple's identifier for advertising (IDFA)
  // or Android's advertising identifier. When the advertising_id is an IDFA,
  // it is the plaintext returned by iOS's [ASIdentifierManager
  // advertisingIdentifier]. For hashed_idfa, the plaintext is the MD5 hash of
  // the IDFA.  Only one of the two fields will be available, depending on the
  // version of the SDK making the request.  Later SDKs provide unhashed values.
  optional bytes advertising_id = 1;
  optional bytes hashed_idfa = 2;
}

Bạn cần giải trình tự mã này bằng cách sử dụng ParseFromString() như mô tả trong tài liệu về vùng đệm giao thức C++.

Để biết thông tin chi tiết về các trường advertising_id và iOS hashed_idfa trên Android, hãy xem bài viết Giải mã mã nhận dạng cho quảng cáoNhắm mục tiêu khoảng không quảng cáo trên ứng dụng dành cho thiết bị di động bằng IDFA.

Thư viện Java

Thay vì triển khai các thuật toán mã hoá để mã hoá và giải mã Giá trị nhận dạng của nhà quảng cáo cho các mạng quảng cáo, bạn có thể sử dụng DoubleClickCrypto.java. Để biết thêm thông tin, hãy xem bài viết Mật mã học.