Déchiffrer les signaux de ciblage hyperlocalisés

Si les éditeurs transmettent à Authorized Buyers des données de localisation plus précises qu'un code postal, Authorized Buyers envoie une zone de géorepérage hyperlocalisée aux acheteurs dans un nouveau champ chiffré: BidRequest.encrypted_hyperlocal_set.

Chronologie

  1. Un utilisateur installe une application mobile avec publicité et accepte que celle-ci accède à la position de l'appareil et la partage avec des tiers. Cette application est également intégrée au SDK Google Ads et envoie la position de cet appareil à Google.
  2. Les serveurs Google génèrent un signal de ciblage hyperlocalisé spécial qui représente une zone de géorepérage autour de l'emplacement de l'appareil, par exemple pour protéger la vie privée des utilisateurs.
  3. Les serveurs Google sérialisent et chiffrent le signal de ciblage hyperlocalisé à l'aide de la clé de sécurité spécifique à chaque acheteur. Notez que le système d'enchères utilise la même clé pour déchiffrer la macro WINNING_PRICE.
  4. Votre système d'enchères déchiffre et désérialise le signal de ciblage hyperlocalisé en un tampon de protocole. Votre enchérisseur peut ensuite analyser le signal et enchérir en conséquence.

Dépendances

Vous aurez besoin d'une bibliothèque de cryptographie compatible avec SHA-1 HMAC, telle que Openssl.

Définition

Un signal de ciblage hyperlocalisé est défini dans le protocole comme suit:

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

Chaque signal de ciblage hyperlocalisé contient un ou plusieurs polygones et un point central. Pour chaque polygone, le signal de ciblage hyperlocalisé contient:

  • La latitude et la longitude de chaque angle du polygone sont transmises de manière séquentielle en tant que champ corners répété.
  • Centre géométrique approximatif de la zone de géorepérage spécifiée dans le champ facultatif center_point.

Structure du signal de ciblage

Le signal de ciblage hyperlocalisé chiffré dans BidRequest.encrypted_hyperlocal_set comporte trois sections:

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

Le tableau d'octets ciphertext est divisé en plusieurs sections de 20 octets, à l'exception que la toute dernière section peut contenir entre 1 et 20 octets. Pour chaque section du byte_array d'origine, le ciphertext de 20 octets correspondant est généré comme suit:

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

|| représente une concaténation.

Définitions

Variable Détails
initialization_vector 16 octets : valeur unique pour l'impression.
encryption_key 32 octets : fournis au moment de la configuration du compte.
integrity_key 32 octets : fournis au moment de la configuration du compte.
byte_array Un objet HyperlocalSet sérialisé en sections de 20 octets.
counter_bytes Valeur d'octets indiquant le numéro ordinal de la section (voir ci-dessous).
final_message Tableau d'octets envoyé via le champ BidRequest.encrypted_hyperlocal_set.
Opérateurs Détails
hmac(key, data) HMAC SHA-1 utilisant key pour chiffrer data.
a || b chaîne a concaténée avec la chaîne b.

Calculer contre_octets

counter_bytes marque l'ordre de chaque section de 20 octets de ciphertext. Notez que la dernière section peut contenir entre 1 et 20 octets inclus. Pour renseigner counter_bytes avec la valeur correcte lorsque vous exécutez votre fonction hmac(), comptez les sections de 20 octets (y compris le reste) et utilisez la table de référence suivante:

Numéro de section Valeur counter_bytes
0 Aucun
1 ... 256 1 octet. La valeur s'incrémente de 0 à 255 de manière séquentielle.
257 ... 512 2 octets. La valeur du premier octet est 0. La valeur du deuxième octet s'incrémente de 0 à 255 de manière séquentielle.
513 ... 768 3 octets. La valeur des deux premiers octets est égale à 0. Les valeurs du dernier octet sont incrémentées de 0 à 255 de manière séquentielle.

La longueur de BidRequest.encrypted_hyperlocal_set ne devrait pas dépasser un kilo-octet, même en tenant compte d'une croissance plus importante. Cependant, counter_bytes peut être aussi long que nécessaire pour prendre en charge un signal de ciblage hyperlocalisé de longueur arbitraire.

Schéma de chiffrement

Le schéma de chiffrement pour le signal de ciblage hyperlocalisé est basé sur le même schéma que celui utilisé pour déchiffrer les confirmations de prix.

  1. Sérialisation : le signal de ciblage hyperlocalisé, qui est une instance de l'objet HyperlocalSet, tel que défini dans le protocole, est d'abord sérialisé via SerializeAsString() en un tableau d'octets.

  2. Chiffrement : le tableau d'octets est ensuite chiffré à l'aide d'un schéma de chiffrement personnalisé conçu pour réduire la surcharge sur la taille tout en assurant une sécurité adéquate. Le schéma de chiffrement utilise un algorithme HMAC à clé pour générer un cache secret basé sur le initialization_vector, qui est unique à l'événement d'impression.

Pseudo-code de chiffrement

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

Schéma de déchiffrement

Votre code de déchiffrement doit 1) déchiffrer le signal de ciblage hyperlocalisé à l'aide de la clé de chiffrement et 2) vérifier les bits d'intégrité à l'aide de la clé d'intégrité. Les clés vous seront fournies lors de la configuration du compte. Il n'existe aucune restriction concernant la structure de votre mise en œuvre. Dans la plupart des cas, vous devez être en mesure de prendre l'exemple de code et de l'adapter à vos besoins.

  1. Générer votre bloc-notes : HMAC(encryption_key, initialization_vector || counter_bytes)
  2. XOR : utilise ce résultat et <xor> avec le texte chiffré pour inverser le chiffrement.
  3. Vérification : la signature d'intégrité transmet 4 octets de HMAC(integrity_key, byte_array || initialization_vector)

Pseudo-déchiffrement

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

Exemple de code C++

Vous trouverez ci-dessous une fonction clé de notre exemple de code de déchiffrement complet.

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

Exemples de clés et de signaux hyperlocalisés

Pour tester et valider votre code:

  1. Convertissez une chaîne contenant 308 caractères hexadécimaux en un tableau de 154 octets. Par exemple, avec la chaîne suivante :
    E2014EA201246E6F6E636520736F7572636501414243C0ADF6B9B6AC17DA218FB50331EDB376701309CAAA01246E6F6E636520736F7572636501414243C09ED4ECF2DB7143A9341FDEFD125D96844E25C3C202466E6F6E636520736F7572636502414243517C16BAFADCFAB841DE3A8C617B2F20A1FB7F9EA3A3600256D68151C093C793B0116DB3D0B8BE9709304134EC9235A026844F276797
    
    convertissez-la en tableau de 154 octets comme suit:
    const char serialized_result[154] = { 0xE2, 0x01, 0x4E, ... };
    
  2. Appelez la méthode BidRequest.ParsePartialFromString() pour désérialiser le tableau de 154 octets dans un tampon de protocole BidRequest.
    BidRequest bid_req;
    bid_req.ParsePartialFromString(serialzed_result);
    
  3. Vérifiez que BidRequest ne comporte que trois champs :
    • encrypted_hyperlocal_set
      Déclaré dans le message BidReqeust.
    • encrypted_advertising_id
      Déclaré dans le message BidReqeust.Mobile.
    • encrypted_hashed_idfa
      Déclaré dans le message BidReqeust.Mobile.

    Exemple :

    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. Utilisez les encryption_key et integrity_key suivants pour déchiffrer les trois champs et vérifier que vous les avez correctement déchiffrés.
    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};
    

Détection des attaques de réponses obsolètes

Pour détecter les attaques de réponses obsolètes, nous vous recommandons de filtrer les réponses avec un horodatage très différent de l'heure du système, après prise en compte des différences de fuseau horaire. Nos serveurs sont définis sur l'heure PST/PDT.

Pour en savoir plus sur l'implémentation, consultez la section "Détecter les attaques de réponse obsolètes" de l'article Déchiffrer les confirmations de prix.

Bibliothèque Java

Au lieu de mettre en œuvre les algorithmes cryptographiques pour encoder et décoder les signaux de ciblage hyperlocalisés, vous pouvez utiliser DoubleClickCrypto.java. Pour en savoir plus, consultez la page Cryptographie.