Descriptografar sinais de segmentação hiperlocais

Se os editores passarem dados de local para dispositivos móveis ao Authorized Buyers mais específicos que um código postal, o Authorized Buyers enviará uma fronteira geográfica virtual aos compradores em um novo campo criptografado: BidRequest.encrypted_hyperlocal_set.

Cronograma

  1. Um usuário instala um app para dispositivos móveis com anúncios e permite que ele acesse e compartilhe o local do dispositivo com terceiros. Esse app também é integrado ao SDK do Google Ads e envia essa localização de dispositivo ao Google.
  2. Os servidores do Google geram um sinal de segmentação hiperlocal especial que representa uma fronteira geográfica virtual ao redor da localização do dispositivo, por exemplo, para proteger a privacidade do usuário.
  3. Os servidores do Google serializam e criptografam o sinal de segmentação hiperlocal usando a chave de segurança específica de cada comprador. O bidder usa a mesma chave para descriptografar a macro WINNING_PRICE.
  4. O bidder descriptografa e desserializa o sinal de segmentação hiperlocal em um buffer de protocolo. Em seguida, o bidder pode analisar o sinal e dar lances adequadamente.

Dependências

Você precisará de uma biblioteca de criptografia compatível com o SHA-1 HMAC, como Openssl.

Definição

Um sinal de segmentação hiperlocal é definido no protocolo da seguinte forma:

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

Cada indicador de segmentação por hiperlocal contém um ou mais polígonos e um ponto central. Para cada polígono, o sinal de segmentação por hiperlocal contém:

  • A latitude e a longitude de cada canto do polígono em sequência, transmitidas como um campo corners repetido.
  • O centro geométrico aproximado da área de fronteira geográfica virtual, transmitido no campo opcional center_point.

Estrutura do indicador de segmentação

O sinal de segmentação hiperlocal criptografado contido em BidRequest.encrypted_hyperlocal_set contém três seções:

  • initialization_vector: 16 bytes.
  • ciphertext: série de seções de 20 bytes.
  • integrity_signature: 4 bytes.
{initialization_vector (16 bytes)}{ciphertext (20-byte sections)}{integrity_signature (4 bytes)}

A matriz de bytes ciphertext é dividida em várias seções de 20 bytes, exceto que a última seção pode conter de 1 a 20 bytes inclusivos. Para cada seção do byte_array original, o ciphertext de 20 bytes correspondente é gerado da seguinte forma:

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

Em que || é concatenação.

Definições

Variável Detalhes
initialization_vector 16 bytes - exclusivos à impressão.
encryption_key 32 bytes - fornecidos na configuração da conta.
integrity_key 32 bytes - fornecidos na configuração da conta.
byte_array Um objeto HyperlocalSet serializado, em seções de 20 bytes.
counter_bytes Valor de byte mostrando o número ordinal da seção, veja abaixo.
final_message Matriz de bytes enviada pelo campo BidRequest.encrypted_hyperlocal_set.
Operadores Detalhes
hmac(key, data) HMAC SHA-1, usando key para criptografar data.
a || b String a concatenada com a string b.

Calcular contador_bytes

counter_bytes marca a ordem de cada seção de 20 bytes da ciphertext. A última seção pode conter de 1 a 20 bytes. Para preencher counter_bytes com o valor correto ao executar a função hmac(), conte as seções de 20 bytes (incluindo o restante) e use a tabela de referência a seguir:

Número da seção Valor counter_bytes
0 Nenhum
1 ... 256 1 byte O valor incrementa de 0 a 255 em sequência.
257...512 2 bytes O valor do primeiro byte é 0. O valor do segundo byte incrementa de 0 a 255 em sequência.
513... 768 3 bytes O valor dos dois primeiros bytes são 0, o valor do último byte incrementa de 0 a 255 em sequência.

Não esperamos que o comprimento de BidRequest.encrypted_hyperlocal_set exceda um kilobyte, mesmo considerando mais crescimento. No entanto, counter_bytes pode ter o tempo necessário para oferecer compatibilidade com um sinal de segmentação hiperlocal de comprimento arbitrário.

Esquema de criptografia

O esquema de criptografia para sinais de segmentação hiperlocal é baseado no mesmo esquema usado para descriptografar confirmações de preços.

  1. Serialização: o sinal de segmentação por hiperlocal, que é uma instância do objeto HyperlocalSet, conforme definido no proto, é primeiro serializado por SerializeAsString() para uma matriz de bytes.

  2. Criptografia: a matriz de bytes é criptografada usando um esquema de criptografia personalizado projetado para minimizar a sobrecarga de tamanho, garantindo a segurança adequada. O esquema de criptografia usa um algoritmo HMAC com chave para gerar um painel secreto com base no initialization_vector, que é exclusivo para o evento de impressão.

Pseudocódigo de criptografia

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

Esquema de descriptografia

O código de descriptografia precisa 1) descriptografar o sinal de segmentação hiperlocal usando a chave de criptografia e 2) verificar os bits de integridade com a chave de integridade. As chaves serão fornecidas a você durante a configuração da conta. Não há restrições sobre como você estrutura sua implementação. Na maioria das vezes, você poderá adaptar o exemplo de código e adaptá-lo de acordo com suas necessidades.

  1. Gere seu bloco: HMAC(encryption_key, initialization_vector || counter_bytes)
  2. XOR: veja este resultado e <xor> com o texto criptografado para inverter a criptografia.
  3. Verificação: a assinatura da integridade transmite quatro bytes de HMAC(integrity_key, byte_array || initialization_vector).

Pseudocódigo de descriptografia

(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)

Exemplo de código em C++

Aqui está uma função-chave do nosso código de exemplo de descriptografia completo.

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');
    }
  }
}

Exemplo de chaves e sinais hiperlocais

Para testar e verificar seu código, faça o seguinte:

  1. Converta uma string contendo 308 caracteres hexadecimais em uma matriz de 154 bytes. Por exemplo, considerando a seguinte string:
    E2014EA201246E6F6E636520736F7572636501414243C0ADF6B9B6AC17DA218FB50331EDB376701309CAAA01246E6F6E636520736F7572636501414243C09ED4ECF2DB7143A9341FDEFD125D96844E25C3C202466E6F6E636520736F7572636502414243517C16BAFADCFAB841DE3A8C617B2F20A1FB7F9EA3A3600256D68151C093C793B0116DB3D0B8BE9709304134EC9235A026844F276797
    
    converta-o para uma matriz de 154 bytes da seguinte maneira:
    const char serialized_result[154] = { 0xE2, 0x01, 0x4E, ... };
    
  2. Chame o método BidRequest.ParsePartialFromString() para desserializar a matriz de 154 bytes em um buffer de protocolo BidRequest.
    BidRequest bid_req;
    bid_req.ParsePartialFromString(serialzed_result);
    
  3. Verifique se o BidRequest tem apenas três campos:
    • encrypted_hyperlocal_set
      Declarado na mensagem BidReqeust.
    • encrypted_advertising_id
      Declarado na mensagem BidReqeust.Mobile.
    • encrypted_hashed_idfa
      Declarado na mensagem BidReqeust.Mobile.

    Exemplo:

    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. Use os seguintes encryption_key e integrity_key para descriptografar os três campos e verificar se eles foram descriptografados corretamente.
    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};
    

Detectar ataques de resposta desatualizadas

Para detectar ataques de resposta desatualizados, recomendamos filtrar as respostas com um carimbo de data/hora que seja significativamente diferente do horário do sistema, depois de considerar as diferenças de fuso horário. Nossos servidores estão configurados para o horário PST/PDT.

Para detalhes sobre implementação, consulte "Como detectar ataques de resposta desatualizada" no artigo Descriptografia das confirmações de preço.

Biblioteca Java

Em vez de implementar os algoritmos de criptomoedas para codificar e decodificar os sinais de segmentação hiperlocal, você pode usar DoubleClickCrypto.java. Para mais informações, consulte Criptografia.