Collections de clés

Tink utilise des ensembles de clés pour permettre la rotation des clés. Formellement, une collection de clés est une liste non vide1 de clés dans laquelle une clé est désignée comme principale (la clé qui est utilisée, par exemple, pour signer et chiffrer de nouveaux textes bruts). De plus, les clés d'une collection de clés reçoivent un ID unique�2 et un état de clé qui permet de désactiver les clés sans les supprimer d'une collection de clés.

Les ensembles de clés sont le principal moyen pour les utilisateurs d'accéder aux clés (via la classe KeysetHandle). Cela garantit que chaque utilisateur dispose d'un code pour gérer plusieurs clés à la fois. Pour la plupart des utilisateurs de la cryptographie, la gestion de plusieurs clés est une nécessité : il doit être possible de changer de clé (les anciennes clés peuvent être divulguées, par exemple), et il n'existe presque jamais de "passage à la clé suivante" atomique qui puisse être appliqué aux machines sur lesquelles le code s'exécute et à tous les textes chiffrés, à l'échelle mondiale et instantanément. L'utilisateur doit donc écrire du code qui fonctionne lorsqu'il passe d'une clé à l'autre.

Exemple : AEAD

Prenons l'exemple d'un keyset AEAD, qui contient plusieurs clés pour la primitive AEAD. Comme expliqué précédemment, chaque clé spécifie de manière unique deux fonctions : \(\mathrm{Enc}\) et \(\mathrm{Dec}\). Le keyset spécifie désormais deux nouvelles fonctions : \(\mathrm{Enc}\) et \(\mathrm{Dec}\) . \(\mathrm{Enc}\) est simplement égal à la fonction \(\mathrm{Enc}\) de la clé primaire du keyset, tandis que la fonction \(\mathrm{Dec}\) tente de déchiffrer avec toutes les clés, en les parcourant dans un certain ordre (voir ci-dessous pour savoir comment Tink améliore les performances de cette fonction).

Il est intéressant de noter que les ensembles de clés sont des clés complètes : ils constituent une description complète des fonctions \(\mathrm{Enc}\) et\(\mathrm{Dec}\) utilisées. Cela signifie que les utilisateurs peuvent écrire une classe qui prend un KeysetHandle comme entrée, exprimant l'idée que la classe a besoin d'une description complète des objets \(\mathrm{Enc}\) et \(\mathrm{Dec}\) pour fonctionner correctement. Cela permet à l'utilisateur d'écrire des API qui communiquent que, pour utiliser cette classe, vous devez me fournir la description d'une primitive cryptographique.

Rotation des clés

Prenons l'exemple d'un utilisateur Tink qui écrit un programme qui obtient d'abord un trousseau de clés à partir d'un KMS, puis crée un objet AEAD à partir de ce trousseau de clés, et enfin utilise cet objet pour chiffrer et déchiffrer des textes chiffrés.

Un tel utilisateur est automatiquement préparé à la rotation des clés et au changement d'algorithme si son choix actuel ne répond plus à la norme.

Il faut toutefois faire preuve d'une certaine prudence lors de l'implémentation de cette rotation de clés : Tout d'abord, le KMS doit ajouter une clé à l'ensemble de clés (mais ne pas encore la définir comme clé primaire). Ensuite, la nouvelle collection de clés doit être déployée sur tous les binaires, de sorte que chaque binaire utilisant cette collection de clés dispose de la clé la plus récente. C'est seulement à ce moment-là que la nouvelle clé doit être définie comme clé primaire, et la collection de clés résultante est à nouveau distribuée à tous les binaires utilisant la collection de clés.

Identifiants clés dans les textes chiffrés

Reprenons l'exemple d'un keyset AEAD. Si cela est fait de manière naïve, le déchiffrement d'un texte chiffré nécessite que Tink tente de déchiffrer avec toutes les clés de la collection de clés, car il n'y a aucun moyen de savoir quelle clé a été utilisée pour chiffrer la collection de clés. Cela peut entraîner une surcharge importante des performances.

Pour cette raison, Tink permet de préfixer les textes chiffrés avec une chaîne de cinq octets dérivée de l'ID. Conformément à la philosophie des "clés complètes" ci-dessus, ce préfixe fait partie de la clé, et tous les textes chiffrés jamais dérivés avec cette clé doivent comporter ce préfixe. Lorsque les utilisateurs créent des clés, ils peuvent choisir si la clé doit utiliser un tel préfixe ou si un format de texte chiffré sans préfixe doit être utilisé.

Lorsqu'une clé se trouve dans un trousseau de clés, Tink calcule ce tag à partir de l'ID de la clé dans le trousseau de clés. Le fait que les ID soient uniques2 dans un ensemble de clés implique que les tags sont uniques. Par conséquent, si seules des clés taguées sont utilisées, il n'y a aucune perte de performances par rapport au déchiffrement avec une seule clé : Tink n'a besoin d'essayer qu'une seule des clés lors du déchiffrement.

Toutefois, comme le tag fait partie de la clé, cela implique également que la clé ne peut se trouver dans un keyset que si elle possède un ID spécifique. Cela a des implications lors de la description de l'implémentation des objets clés dans différentes langues.

Clés avec une exigence d'ID, mais sans préfixe de sortie

Certaines clés doivent avoir un ID spécifique, mais n'ajoutent aucun préfixe à leur sortie. Par exemple, les clés de signature avec la variante NO_PREFIX_WITH_PREHASH_ID (stockées avec le type de préfixe de sortie WITH_ID_REQUIREMENT) génèrent des signatures sans préfixe. Lorsque vous utilisez une telle clé avec la primitive Prehash, Tink écrit l'ID de clé dans la valeur de préhachage, afin qu'un signataire distant sache avec laquelle de ses clés signer.

Comme pour les clés utilisant un préfixe, une telle clé ne peut figurer que dans un trousseau de clés portant cet ID. L'ID de clé dans la valeur préhachée est une métadonnée brute, tout comme le préfixe de sortie : la signature ne l'associe pas et les vérificateurs ne la voient jamais. Pour la mise en page au niveau des octets, consultez Format filaire Tink.


  1. Certaines parties de Tink traitent toujours les Keysets comme un ensemble. Toutefois, ce paramètre doit être modifié. En effet, l'ordre est généralement important. Par exemple, prenons le cycle de vie typique d'une rotation de clé avec Aead. Tout d'abord, une clé est ajoutée à un ensemble de clés. Cette clé n'est pas encore définie comme clé principale, mais elle est active. Ce nouveau trousseau de clés est déployé sur tous les binaires. Une fois que tous les binaires connaissent la nouvelle clé, celle-ci devient la clé primaire (c'est seulement à ce moment-là qu'il est sûr de l'utiliser). Lors de cette deuxième étape, la rotation des clés doit connaître la dernière clé ajoutée. ↩

  2. Pour assurer la compatibilité avec une bibliothèque interne Google, Tink permet d'avoir des ensembles de clés dans lesquels les ID sont répétés. Cette compatibilité sera supprimée à l'avenir. ↩