지역 밀착형 타겟팅 신호 복호화

게시자가 우편번호보다 구체적인 모바일 위치 데이터를 Authorized Buyers에 전달하면 Authorized Buyers는 암호화된 새 필드인 BidRequest.encrypted_hyperlocal_set을 통해 구매자에게 지오펜싱을 전송합니다.

일정

  1. 사용자가 광고 지원 모바일 앱을 설치하고 앱이 기기 위치에 액세스하고 이를 제3자와 공유하는 데 동의합니다. 이 앱은 Google Ads SDK와도 통합되며 이 기기 위치를 Google로 전송합니다.
  2. Google 서버는 기기 위치 주변의 지오펜싱을 나타내는 특수한 지역 밀착형 타겟팅 신호를 생성합니다(예: 사용자의 개인 정보 보호).
  3. Google 서버는 각 구매자별 보안 키를 사용하여 지역 밀착형 타겟팅 신호를 직렬화하고 암호화합니다. 입찰자는 동일한 키를 사용하여 WINNING_PRICE 매크로를 복호화합니다.
  4. 입찰자가 지역 밀착형 타겟팅 신호를 프로토콜 버퍼로 복호화하고 역직렬화합니다. 그러면 입찰자가 신호를 분석하고 그에 따라 입찰할 수 있습니다.

종속 항목

SHA-1 HMAC를 지원하는 암호화 라이브러리(예: Openssl)가 필요합니다.

정의

지역 밀착형 타겟팅 신호는 다음과 같이 proto에 정의됩니다.

// 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 20바이트 섹션 내 직렬화된 HyperlocalSet 객체
counter_bytes 섹션의 서수 번호를 보여주는 바이트 값입니다. 아래를 참조하세요.
final_message BidRequest.encrypted_hyperlocal_set 필드를 통해 전송된 바이트 배열
연산자 세부정보
hmac(key, data) SHA-1 HMAC, key를 사용하여 data를 암호화.
a || b a 문자열은 b 문자열과 연결됩니다.

카운터_바이트 계산

counter_bytesciphertext의 각 20바이트 섹션 순서를 표시합니다. 마지막 섹션에는 1~20바이트가 포함될 수 있습니다. hmac() 함수를 실행할 때 counter_bytes를 올바른 값으로 채우려면 20바이트 섹션(나머지 포함)을 계산하고 다음 참조 테이블을 사용합니다.

섹션 번호 counter_bytes
0 없음
1... 256 1바이트 값은 0부터 255까지 순차적으로 증가합니다.
257...512 2바이트 첫 번째 바이트 값은 0이고 두 번째 바이트 값은 0부터 255까지 순차적으로 증가합니다.
513... 768 3바이트 처음 2바이트 값은 0이고 마지막 바이트 값은 0에서 255까지 순차적으로 증가합니다.

BidRequest.encrypted_hyperlocal_set의 길이가 1KB를 초과할 것으로 예상되지 않으며 심지어 추가 성장을 고려합니다. 그럼에도 불구하고 counter_bytes는 임의의 길이의 지역 밀착형 타겟팅 신호를 지원하는 데 필요한 만큼 길 수 있습니다.

암호화 스키마

지역 밀착형 타겟팅 신호의 암호화 스키마는 가격 확인 복호화에 사용된 것과 동일한 체계를 기반으로 합니다.

  1. 직렬화: proto에 정의된 HyperlocalSet 객체의 인스턴스인 지역 밀착형 타겟팅 신호는 먼저 SerializeAsString()를 통해 바이트 배열로 직렬화됩니다.

  2. 암호화: 그런 다음 적절한 보안을 보장하면서 크기 오버헤드를 최소화하도록 설계된 커스텀 암호화 스킴을 사용하여 바이트 배열을 암호화합니다. 암호화 스키마는 키 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) 무결성 키로 무결성 비트를 확인해야 합니다. 키는 계정 설정 중에 제공됩니다. 구현 방식에는 제한이 없습니다. 대부분의 경우 샘플 코드를 필요에 따라 조정할 수 있습니다.

  1. 패드 생성: HMAC(encryption_key, initialization_vector || counter_bytes)
  2. XOR: 이 결과와 <xor>를 암호문과 함께 사용하여 암호화를 반대로 진행합니다.
  3. 확인: 무결성 서명이 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');
    }
  }
}

샘플 지역 밀착형 신호 및 키

코드를 테스트하고 확인하는 방법은 다음과 같습니다.

  1. 308자리 16진수 문자가 포함된 문자열을 154바이트 배열로 변환합니다. 예를 들어 다음 문자열이 표시됩니다.
    E2014EA201246E6F6E636520736F7572636501414243C0ADF6B9B6AC17DA218FB50331EDB376701309CAAA01246E6F6E636520736F7572636501414243C09ED4ECF2DB7143A9341FDEFD125D96844E25C3C202466E6F6E636520736F7572636502414243517C16BAFADCFAB841DE3A8C617B2F20A1FB7F9EA3A3600256D68151C093C793B0116DB3D0B8BE9709304134EC9235A026844F276797
    
    다음과 같이 154바이트 배열로 변환합니다.
    const char serialized_result[154] = { 0xE2, 0x01, 0x4E, ... };
    
  2. BidRequest.ParsePartialFromString() 메서드를 호출하여 154바이트 배열을 BidRequest 프로토콜 버퍼로 역직렬화합니다.
    BidRequest bid_req;
    bid_req.ParsePartialFromString(serialzed_result);
    
  3. 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 }
    
  4. 다음 encryption_keyintegrity_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};
    

비활성 응답 공격 감지

비활성 응답 공격을 감지하려면 시간대 차이를 고려한 후 시스템 시간과 크게 다른 타임스탬프로 응답을 필터링하는 것이 좋습니다. 서버가 PST/PDT로 설정됩니다.

구현에 관한 자세한 내용은 가격 확인 복호화 도움말의 '오래된 응답 공격 감지'를 참고하세요.

자바 라이브러리

지역 밀착형 타겟팅 신호를 인코딩 및 디코딩하기 위해 암호화 알고리즘을 구현하는 대신 DoubleClickCrypto.java를 사용할 수 있습니다. 자세한 내용은 암호화를 참조하세요.