如果發布商將行動位置資料傳送給比郵遞區號更明確的 Authorized Buyers,Authorized Buyers 會將新的加密欄位傳送給買方的 ##9;hyperlocal'地理圍欄。BidRequest.encrypted_hyperlocal_set
時程
- 使用者安裝含廣告功能的行動應用程式,並同意應用程式存取裝置及與第三方分享裝置位置資訊。這個應用程式也整合了 Google Ads SDK,並將這部裝置位置傳送給 Google。
- Google 伺服器會產生特殊的超在地指定目標信號,代表裝置位置資訊周圍的地理圍欄,例如為了保護使用者的隱私權。
- Google 伺服器會使用每個買方專屬的安全金鑰,序列化及加密超本機指定目標信號。請注意,您的出價工具會使用相同的鍵解密 WINNING_PRICE 巨集。
- 您的出價方會將超在地指定目標信號解密,並序列化為通訊協定緩衝區。這樣出價方就能分析信號,並據此出價。
依附元件
您必須使用支援 SHA-1 HMAC 的加密編譯程式庫,例如 Openssl。
定義
通訊協定中預設的超在地指定目標訊號如下:
// 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;
每個超在地指定目標信號都包含一或多個多邊形和中心點。每個多邊形的超在地指定目標信號都包含:
- 多邊形中每個角落的緯度與經度,以重複的
corners
欄位傳遞。 - 地理圍欄區域的約略幾何圖形中心,透過選填的
center_point
欄位傳遞。
指定目標信號的結構
BidRequest.encrypted_hyperlocal_set
包含的加密超本機指定目標信號包含 3 個部分:
initialization_vector
:16 個位元組。ciphertext
:20 位元組的系列。integrity_signature
:4 個位元組。
{initialization_vector (16 bytes)}{ciphertext (20-byte sections)}{integrity_signature (4 bytes)}
ciphertext
位元組陣列分為多個 20 位元組區段,但最後一段可能包含 1 至 20 位元組 (含首尾)。原始 byte_array
中的每個部分,對應的 20 位元組 ciphertext
會產生:
<byte_array <xor> HMAC(encryption_key, initialization_vector || counter_bytes)>
「||
」的串連位置。
定義
變數 | 詳細資料 |
---|---|
initialization_vector |
16 位元組 - 曝光專屬的。 |
encryption_key |
32 個位元組 - 在帳戶設定時提供。 |
integrity_key |
32 個位元組 - 在帳戶設定時提供。 |
byte_array |
序列化的 HyperlocalSet 物件,以 20 位元組為單位。 |
counter_bytes |
顯示區段序數的位元組值,如下所示。 |
final_message |
透過 BidRequest.encrypted_hyperlocal_set 欄位傳送的位元組陣列。 |
運算子 | 詳細資料 |
---|---|
hmac(key, data) |
SHA-1 HMAC,使用 key 加密 data 。 |
a || b |
字串 a 與字串 b 串連。 |
計算 count_bytes
counter_bytes
會標示 ciphertext
中每個 20 位元組區段的順序。請注意,上一節可能包含 1 到 20 位元組 (含)。如要在執行 hmac()
函式時以正確的值填入 counter_bytes
,請計算 20 位元組的部分 (包括其餘部分),並使用以下參考表格:
區段編號 | counter_bytes 值 |
---|---|
0 天 | 無 |
1 ... 256 | 1 個位元組。這個值會從 0 依序遞增 255。 |
257 ... 512 | 2 個位元組。第一個位元組的值是 0,第二個位元組的值則是依序從 0 到 255。 |
513 ... 768 | 3 個位元組。前兩個位元組的值是 0,最後一個位元組的值會依序從 0 到 255。 |
我們不預期 BidRequest.encrypted_hyperlocal_set
的長度不會超過 1 KB,即使在考量方面仍有進一步的成長也是如此。然而,counter_bytes
可以視需要支援任意長度的超在地指定目標信號。
加密配置
超在地指定目標訊號的加密配置與解密價格確認所使用的配置相同。
序列化:超在地指定目標信號,是 ProtoSet 中定義的超本機物件物件,會先透過
SerializeAsString()
序列化為位元組陣列。加密:位元組陣列接著會使用自訂加密配置來加密,藉此盡量減少大小負擔,並確保充分安全性。加密配置會使用金鑰化的 HMAC 演算法,根據曝光事件特有的
initialization_vector
產生密鑰區。
加密虛擬程式碼
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
解密配置
您的解密程式碼必須:1) 使用加密金鑰解密超在地指定目標訊號,以及 2) 使用完整性金鑰驗證完整性位元。我們會在帳戶設定過程中提供金鑰。實作結構沒有限制。大部分情況下,您應該可以取得程式碼範例,並視需要進行調整。
- 產生觸控板:
HMAC(encryption_key, initialization_vector || counter_bytes)
- XOR:選取這個結果並搭配
<xor>
和密文來撤銷加密。 - 驗證:完整性簽名已通過 4 個位元組的
HMAC(integrity_key, byte_array || initialization_vector)
解密虛擬程式碼
(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)
C++ 程式碼範例
以下提供完整的解密程式碼範例中的重要函式,
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'); } } }
超在地訊號和金鑰範例
如要測試及驗證程式碼,請按照下列指示操作:
- 將含有 308 個十六進位字元的字串轉換為 154 個位元組的陣列。例如,假設有以下字串:
E2014EA201246E6F6E636520736F7572636501414243C0ADF6B9B6AC17DA218FB50331EDB376701309CAAA01246E6F6E636520736F7572636501414243C09ED4ECF2DB7143A9341FDEFD125D96844E25C3C202466E6F6E636520736F7572636502414243517C16BAFADCFAB841DE3A8C617B2F20A1FB7F9EA3A3600256D68151C093C793B0116DB3D0B8BE9709304134EC9235A026844F276797
並將其轉換為 154 位元組陣列,如下所示:const char serialized_result[154] = { 0xE2, 0x01, 0x4E, ... };
- 呼叫
BidRequest.ParsePartialFromString()
方法,將 154 位元組陣列反序列化為BidRequest
通訊協定緩衝區。BidRequest bid_req; bid_req.ParsePartialFromString(serialzed_result);
- 確認
BidRequest
只有 3 個欄位:encrypted_hyperlocal_set
在BidReqeust
訊息中宣告。encrypted_advertising_id
在BidReqeust.Mobile
訊息中宣告。encrypted_hashed_idfa
在BidReqeust.Mobile
訊息中宣告。
例如:
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 }
- 使用下列
encryption_key
和integrity_key
來解密 3 個欄位,並確認您已正確解密。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};
偵測過時回應攻擊
如要偵測過時的回應攻擊,建議您在考量時區差異後,篩選出時間戳記與系統時間截然不同的回應。我們的伺服器已設為太平洋標準時間/PDT 時間。
如需實作詳細資料,請參閱「解密價格確認」一文中的「偵測過時的回應攻擊」一節。
Java 程式庫
您不必實作加密演算法,以將超在地指定目標信號進行編碼及解碼,您可以使用 DoubleClickCrypto.java。詳情請參閱密碼編譯。