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 Các thẻ JavaScript để điền quảng cáo thông qua Authorized Buyers đủ đ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 %%EXTRA_TAG_DATA%% hoặc Macro %%ADVERTISING_IDENTIFIER%% trong thẻ JavaScript được quản lý của Authorized Buyers. 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 Tái tiếp thị bằng IDFA hoặc Mã nhận dạng cho quảng cáo để biết thông tin chi tiết trên vùng đệm proto đã mã hoá %%ADVERTISING_IDENTIFIER%% MobileAdvertisingId có thể được giải mã theo cách tương tự.

Dòng thời gian

  1. Mạng quảng cáo cập nhật các thẻ trong ứng dụng JavaScript thông qua giao diện người dùng Authorized Buyers, thêm vào macro %%EXTRA_TAG_DATA%% như được giải thích bên dưới.
  2. Tại thời điểm phân phát, ứng dụng yêu cầu một quảng cáo từ Authorized Buyers thông qua SDK Quảng cáo của Google trên thiết bị di động, trong khi chuyển mã 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 %%EXTRA_TAG_DATA%% được điền bằng 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, thực hiện lệnh gọi đến Mạng quảng cáo để giành chiến thắng quảng cáo.
  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 một bytestring bằng WebSafeBase64.
    2. Hãy giải mã mã bằng giao thứ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. Công cụ WebSafeBase64 bộ mã hoá.
  2. Một thư viện mật mã hỗ trợ HMAC SHA-1, chẳng hạn như Openssl.
  3. Giao thức Google trình biên dịch vùng đệm.

Giải mã chuỗi websafe

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

Trước khi thử do đó, 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 giao diện OpenSSL BIO_f_base64() của dự án và là một phần trong mẫu của Google mã giải mã.

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<char*>(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 đã mã hoá

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

  • initialization_vector: 16 byte.
  • ciphertext: chuỗi gồm các phần có kích thước 20 byte.
  • integrity_signature: 4 byte.
{initialization_vector (16 bytes)}{ciphertext (20-byte sections)}{integrity_signature (4 bytes)}

Mảng ciphertext được chia thành nhiều 20 byte ngoại trừ phần cuối cùng có thể có giữa 1 và 20 byte. Đối với mỗi phần của nội dung gốc byte_array, ciphertext 20 byte tương ứng được tạo là:

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

trong đó || là phép nối.

Định nghĩa

Biến Thông tin chi tiết
initialization_vector 16 byte – duy nhất cho 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 chuyển đổi tuần tự trong các phần có kích thước 20 byte.
counter_bytes Giá trị byte cho biết số thứ tự của phần này, hãy xem bên dưới.
final_message 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) HMAC SHA-1, sử dụng key để mã hoá data.
a || b chuỗi a nối với chuỗi b.

Tính byte bộ đếm

counter_bytes đánh dấu thứ tự của từng phần 20 byte của ciphertext 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 có kích thước 20 byte (bao gồm 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 dần từ 0 đến 255.
257 ... 512 2 byte. Giá trị của byte đầu tiên là 0, giá trị của byte thứ hai tăng theo tuần tự từ 0 đế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 theo tuần tự từ 0 đến 255.

Trở lại đầu trang

Giao thức mã hoá

Lược đồ mã hóa này dựa trên chính lược đồ dùng để giải mã tín hiệu nhắm mục tiêu siêu địa phương.

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

  2. Mã hoá: Sau đó, mảng byte được mã hoá bằng được thiết kế để giảm thiểu hao tổn kích thước trong khi vẫn đảm bảo có mức bảo mật vừa đủ. Giao thức mã hoá sử dụng thuật toán HMAC có khoá để tạo một khoá bí mật dựa trên initialization_vector (chỉ dành 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

Giao thứ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 cách sử dụng mã và 2) xác minh bit tính toàn vẹn bằng khoá toàn vẹn. Các khoá sẽ là được cung cấp cho bạn trong quá trình thiết lập tài khoản. Không có bất kỳ hạn chế nào về cách bạn cấu trúc quá trình triển khai của bạn. Trong hầu hết trường hợp, bạn sẽ có thể tận dụng mã mẫu và điều chỉnh mã đó theo nhu cầu của bạn.

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

Giải mã mã giả

// 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

Dưới đây là một chức năng chính trong giải mã mã ví dụ.

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 bạn đã giải mã và giải mã dữ liệu được truyền vào %%EXTRA_TAG_DATA%% thân mến, bạn đã sẵn sàng giải tuần tự vùng đệm giao thức và lấy mã 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 sẽ cần giải tuần tự 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 chi tiết về advertising_id trên Android và iOS Trường hashed_idfa, xem phần Giải mã Mã nhận dạng cho quảng cáoNhắm mục tiêu đến ứng dụng di động khoảng không quảng cáo có IDFA.

Thư viện Java

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