Giải mã tín hiệu nhắm mục tiêu siêu địa phương

Nếu nhà xuất bản chuyển dữ liệu vị trí trên thiết bị di động tới Authorized Buyers cụ thể hơn so với mã bưu chính, Authorized Buyers sẽ gửi khoanh vùng địa lý "người dùng cục bộ" cho người mua trong một trường mã hoá mới: BidRequest.encrypted_hyperlocal_set.

Tiến trình

  1. Người dùng cài đặt một ứng dụng dành cho thiết bị di động chứa quảng cáo và đồng ý cho phép ứng dụng đó truy cập và chia sẻ thông tin vị trí của thiết bị với bên thứ ba. Ứng dụng này cũng được tích hợp với SDK quảng cáo của Google và gửi vị trí thiết bị này cho Google.
  2. Các máy chủ của Google tạo ra một tín hiệu nhắm mục tiêu siêu địa phương đặc biệt để đại diện cho một khoanh vùng địa lý xung quanh vị trí của thiết bị, chẳng hạn như để bảo vệ quyền riêng tư của người dùng.
  3. Máy chủ của Google chuyển đổi tuần tự và mã hóa tín hiệu nhắm mục tiêu siêu địa phương bằng cách sử dụng khóa bảo mật dành riêng cho từng người mua. Xin lưu ý rằng người đặt giá thầu của bạn dựa trên cùng một khóa để giải mã macro JCT_PRICE.
  4. Người đặt giá thầu sẽ giải mã và giải tuần tự hoá tín hiệu nhắm mục tiêu siêu địa phương vào vùng đệm giao thức. Sau đó, người đặt giá thầu có thể phân tích tín hiệu và đặt giá thầu cho phù hợp.

Phần phụ thuộc

Bạn sẽ cần một thư viện mật mã hỗ trợ SHA-1 HMAC, chẳng hạn như Openssl.

Định nghĩa

Tín hiệu nhắm mục tiêu siêu địa phương được xác định trong proto như sau:

// A hyperlocal targeting location when available.
//
message Hyperlocal {
  // A location on the Earth's surface.
  //
  message Point {
    optional float latitude = 1;
    optional float longitude = 2;
  }

  // The mobile device can be at any point inside the geofence polygon defined
  // by a list of corners.  Currently, the polygon is always a parallelogram
  // with 4 corners.
  repeated Point corners = 1;
}

message HyperlocalSet {
  // This field currently contains at most one hyperlocal polygon.
  repeated Hyperlocal hyperlocal = 1;

  // The approximate geometric center of the geofence area.  It is calculated
  // exclusively based on the geometric shape of the geofence area and in no
  // way indicates the mobile device's actual location within the geofence
  // area. If multiple hyperlocal polygons are specified above then
  // center_point is the geometric center of all hyperlocal polygons.
  optional Hyperlocal.Point center_point = 2;
}

// Hyperlocal targeting signal when available, encrypted as described at
// https://developers.google.com/authorized-buyers/rtb/response-guide/decrypt-hyperlocal
optional bytes encrypted_hyperlocal_set = 40;

Mỗi tín hiệu nhắm mục tiêu siêu địa phương chứa một hoặc nhiều đa giác và một điểm giữa. Đối với mỗi đa giác, tín hiệu nhắm mục tiêu siêu địa phương chứa:

  • Vĩ độ và kinh độ của mỗi góc của đa giác tuần tự, được chuyển dưới dạng một trường corners lặp lại.
  • Trung tâm hình học gần đúng của khu vực khoanh vùng địa lý, được chuyển vào trường center_point không bắt buộc.

Cấu trúc của tín hiệu nhắm mục tiêu

Tín hiệu nhắm mục tiêu siêu địa phương đã mã hoá có trong BidRequest.encrypted_hyperlocal_set chứa 3 phần:

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

Mảng ciphertext byte được chia thành nhiều phần 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 thành:

<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 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 Một đối tượng HyperlocalSet chuyển đổi tuần tự, trong các phần 20 byte.
counter_bytes Giá trị byte hiển thị số thứ tự của phần, hãy xem bên dưới.
final_message Mảng byte được gửi qua trường BidRequest.encrypted_hyperlocal_set.
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 toán đếm_byte

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

Mã số mục Giá trị counter_bytes
0 Không có
1... 256 1 byte. Giá trị này tăng 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 từ 0 đến 255 theo tuần tự.
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 từ 0 đến 255 theo tuần tự.

Chúng tôi dự kiến độ dài của BidRequest.encrypted_hyperlocal_set sẽ không vượt quá một kilobyte, thậm chí còn có tính đến sự phát triển hơn nữa. Tuy nhiên, counter_bytes có thể miễn là cần thiết để hỗ trợ tín hiệu nhắm mục tiêu siêu địa phương có độ dài tùy ý.

Lược đồ mã hóa

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

  1. Tuần tự hoá: Tín hiệu nhắm mục tiêu siêu địa phương, là một thực thể của đối tượng HyperlocalSet như được xác định trong proto, sẽ được chuyển đổi tuần tự thông qua SerializeAsString() thành một mảng byte.

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

Mã giả

byte_array = SerializeAsString(HyperlocalSet 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ã tín hiệu nhắm mục tiêu siêu địa phương bằng khoá mã hoá và 2) xác minh các bit tính toàn vẹn bằng khoá 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 đối với cách bạn tổ chức triển khai của mình. Trong hầu hết trường hợp, bạn 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à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 mật mã để đảo ngược phương thức mã hoá.
  3. Xác minh: Chữ ký về tính toàn vẹn sẽ truyền 4 byte HMAC(integrity_key, byte_array || initialization_vector)

Mã giả giải mã

(initialization_vector, ciphertext, integrity_signature) = final_message // split up according to length rules
pad = hmac(encryption_key, initialization_vector || counter_bytes)  // for each 20-byte section of ciphertext
byte_array = ciphertext <xor> pad // for each 20-byte section of ciphertext
confirmation_signature = hmac(integrity_key, byte_array || initialization_vector)
success = (confirmation_signature == integrity_signature)

Mã C++ mẫu

Sau đây là một hàm chính trong mã mẫu 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');
    }
  }
}

Ví dụ về khóa và tín hiệu siêu địa phương

Để kiểm tra và xác minh mã của bạn:

  1. Chuyển đổi một chuỗi chứa 308 ký tự hex thành một mảng 154 byte. Ví dụ: giả sử chuỗi sau:
    E2014EA201246E6F6E636520736F7572636501414243C0ADF6B9B6AC17DA218FB50331EDB376701309CAAA01246E6F6E636520736F7572636501414243C09ED4ECF2DB7143A9341FDEFD125D96844E25C3C202466E6F6E636520736F7572636502414243517C16BAFADCFAB841DE3A8C617B2F20A1FB7F9EA3A3600256D68151C093C793B0116DB3D0B8BE9709304134EC9235A026844F276797
    
    hãy chuyển đổi nó thành một mảng 154 byte như sau:
    const char serialized_result[154] = { 0xE2, 0x01, 0x4E, ... };
    
  2. Gọi phương thức BidRequest.ParsePartialFromString() để giải tuần tự mảng 154 byte vào vùng đệm giao thức BidRequest.
    BidRequest bid_req;
    bid_req.ParsePartialFromString(serialzed_result);
    
  3. Xác minh rằng BidRequest chỉ có 3 trường:
    • encrypted_hyperlocal_set
      Được khai báo trong thông báo BidReqeust.
    • encrypted_advertising_id
      Được khai báo trong thông báo BidReqeust.Mobile.
    • encrypted_hashed_idfa
      Được khai báo trong thông báo BidReqeust.Mobile.

    Ví dụ:

    encrypted_hyperlocal_set:(
        {  100,  100 },
        {  200, -300 },
        { -400,  500 },
        { -600, -700 },)
    encrypted_advertising_id: { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 }
    encrypted_hashed_idfa : { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0xF1 }
    
  4. Sử dụng encryption_keyintegrity_key sau đây để giải mã 3 trường và xác minh rằng bạn giải mã chúng chính xác.
    encryption_key = {0x02, 0xEE, 0xa8, 0x3c, 0x6c, 0x12, 0x11, 0xe1, 0x0b,
        0x9f, 0x88, 0x96, 0x6c, 0xee, 0xc3, 0x49, 0x08, 0xeb, 0x94, 0x6f, 0x7e,
        0xd6, 0xe4, 0x41, 0xaf, 0x42, 0xb3, 0xc0, 0xf3, 0x21, 0x81, 0x40};
    
    integrity_key = {0xbf, 0xFF, 0xec, 0x55, 0xc3, 0x01, 0x30, 0xc1, 0xd8,
        0xcd, 0x18, 0x62, 0xed, 0x2a, 0x4c, 0xd2, 0xc7, 0x6a, 0xc3, 0x3b, 0xc0,
        0xc4, 0xce, 0x8a, 0x3d, 0x3b, 0xbd, 0x3a, 0xd5, 0x68, 0x77, 0x92};
    

Phát hiện cuộc tấn công phản hồi cũ

Để phát hiện các cuộc tấn công phản hồi lỗi thời, bạn nên lọc các phản hồi có dấu thời gian khác đáng kể với thời gian của hệ thống sau khi tính đến chênh lệch múi giờ. Máy chủ của chúng tôi được đặt theo giờ PST/PDT.

Để biết thông tin chi tiết về cách triển khai, hãy xem bài viết "Phát hiện cuộc tấn công phản hồi cũ" trong bài viết Giải mã xác nhận giá.

Thư viện Java

Thay vì triển khai các thuật toán mã hoá để mã hoá và giải mã tín hiệu nhắm mục tiêu siêu cục bộ, bạn có thể sử dụng DoubleClickCrypto.java. Để biết thêm thông tin, hãy xem nội dung Mật mã.