Como descriptografar identificadores de anunciantes para redes de publicidade

Redes de anúncios que usam As tags JavaScript para preencher anúncios com o Authorized Buyers estão qualificadas para receber identificadores de anunciantes para dispositivos Android e iOS. As informações são enviadas por %%EXTRA_TAG_DATA%% ou %%ADVERTISING_IDENTIFIER%% macro na tag JavaScript gerenciada pelo Authorized Buyers. O restante desta seção se concentra em extrair %%EXTRA_TAG_DATA%%, mas veja Remarketing com o IDFA ou o ID de publicidade para mais detalhes no buffer proto criptografado %%ADVERTISING_IDENTIFIER%% MobileAdvertisingId que podem ser descriptografados de maneira análoga.

Cronograma

  1. A rede de publicidade atualiza as tags JavaScript no aplicativo pela interface do Authorized Buyers, adicionando a macro %%EXTRA_TAG_DATA%%, conforme explicado abaixo.
  2. No momento da veiculação, o app solicita um anúncio do Authorized Buyers por meio do SDK dos anúncios para dispositivos móveis do Google, e passando o identificador do anunciante com segurança.
  3. O app recebe de volta a tag JavaScript com o %%EXTRA_TAG_DATA%%. preenchida com o buffer de protocolo da rede de publicidade criptografado que contém esse identificador.
  4. O aplicativo executa essa tag, fazendo uma chamada à rede de publicidade para o anúncio vencedor anúncio.
  5. Para usar (monetizar) essas informações, a rede de publicidade precisa processar o buffer de protocolo:
    1. Decodifique a string segura para a Web novamente em uma string de bytes com o WebSafeBase64.
    2. Faça a descriptografia usando o esquema descrito abaixo.
    3. Desserialize o proto e obtenha o ID do anunciante de ExtraTagData.advertising_id ou ExtraTagData.hashed_idfa.

Dependências

  1. A classe WebSafeBase64 codificador.
  2. Uma biblioteca de criptografia compatível com SHA-1 HMAC, como a Openssl.
  3. O protocolo do Google compilador de buffer.

Decodificar string segura para a Web

Como as informações enviadas pela macro %%EXTRA_TAG_DATA%% precisa ser enviada por um URL, os servidores do Google o codificam com base64 segura para a Web (RFC 3548).

Antes de tentar decodificação, portanto, você deve decodificar os caracteres ASCII de volta em um uma string de bytes. O exemplo de código C++ abaixo é baseado no OpenSSL BIO_f_base64() do projeto e faz parte do exemplo do Google o código de descriptografia.

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

Estrutura de strings de bytes criptografadas

Depois de decodificar os caracteres ASCII novamente em uma string de bytes, você já pode para descriptografá-lo. A string de bytes criptografada 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 ciphertext bytes é dividida em vários bytes de 20 bytes com exceção de que a última seção pode conter entre 1 e 20 bytes inclusive. Para cada seção do original byte_array, o ciphertext de 20 bytes correspondente é gerado como:

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

em que || é a concatenação.

Definições

Variável Detalhes
initialization_vector 16 bytes: exclusivo da impressão.
encryption_key 32 bytes: fornecido na configuração da conta.
integrity_key 32 bytes: fornecido na configuração da conta.
byte_array Um objeto ExtraTagData serializado, em seções de 20 bytes.
counter_bytes Valor de byte mostrando o número ordinal da seção, conforme abaixo.
final_message Matriz total de bytes enviada pela macro %%EXTRA_TAG_DATA%% (menos a codificação WebSafeBase64).
Operadores Detalhes
hmac(key, data) SHA-1 HMAC, usando key para criptografar data.
a || b string a concatenada com a string b.

Calcular count_bytes

counter_bytes marca a ordem de cada seção de 20 bytes do ciphertext. A última seção pode conter entre 1 e 20 bytes inclusive. 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 seguinte tabela de referência:

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

Voltar ao início

Esquema de criptografia

O esquema de criptografia é baseado no mesmo esquema usado para descriptografar o sinal de segmentação por hiperlocal.

  1. Serialização: uma instância do objeto ExtraTagData como definido no buffer de protocolo é serializado pela SerializeAsString() em uma matriz de bytes.

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

Pseudocódigo de criptografia

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

Esquema de descriptografia

Seu código de descriptografia precisa 1) descriptografar o buffer de protocolo usando a e 2) verificar os bits de integridade com a chave de integridade. As chaves serão durante a configuração da conta. Não há restrições sobre como você estruturar sua implementação. Na maioria das vezes, você deve ser capaz de 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: pegue este resultado e <xor> com o o texto criptografado para reverter a criptografia.
  3. Verificar: a assinatura de integridade transmite 4 bytes de HMAC(integrity_key, byte_array || initialization_vector)

Pseudocódigo de descriptografia

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

Exemplo de código C++

Incluída aqui uma função-chave de nossa lista descriptografia exemplo de código.

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

Extrair dados do buffer de protocolo da rede de publicidade

Depois de decodificar e descriptografar os dados transmitidos %%EXTRA_TAG_DATA%%, tudo pronto para desserializar o buffer de protocolo e conseguir o identificador do anunciante para segmentação.

Se você não estiver familiarizado com buffers de protocolo, comece com nossa documentação.

Definição

Nosso buffer de protocolo da rede de publicidade é definido assim:

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

É preciso desserializá-lo usando ParseFromString(), conforme descrito nas Documentação do buffer de protocolo C++.

Para mais detalhes sobre o Android advertising_id e o iOS hashed_idfa campo, consulte Descriptografar ID de publicidade e Segmentação de app para dispositivos móveis inventário com o IDFA.

Biblioteca Java

Em vez de implementar os algoritmos de criptografia para codificar e decodificar os identificadores de anunciante para redes de publicidade, é possível usar DoubleClickCrypto.java. Para mais informações, consulte Criptografia.