Tink utilise des ensembles de clés pour permettre la rotation des clés. Formellement, un ensemble de clés est une liste1 non vide de clés dans laquelle une clé est désignée comme principale (celle qui est utilisée, par exemple, pour signer et chiffrer de nouveaux textes bruts). De plus, les clés d'un ensemble de clés reçoivent un ID unique2 et un état de clé qui permet de les désactiver sans les supprimer d'un ensemble 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 permettant de 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 peut être appliqué aux machines sur lesquelles le code s'exécute et à tous les textes chiffrés, de manière globale et en un instant. Par conséquent, l'utilisateur doit écrire du code qui fonctionne lorsqu'on passe d'une touche à l'autre.
Exemple: AEAD
Prenons l'exemple d'un ensemble de clés 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 jeu de clés spécifie désormais également deux nouvelles fonctions: \(\mathrm{Enc}\) et \(\mathrm{Dec}\) - \(\mathrm{Enc}\) équivaut simplement à la fonction \(\mathrm{Enc}\) de la clé primaire du jeu de clés, 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 opération).
Il est intéressant de noter que les ensembles de clés sont des clés complètes : il s'agit d'une description complète des fonctions \(\mathrm{Enc}\) et\(\mathrm{Dec}\) utilisées. Cela signifie que les utilisateurs peuvent écrire une classe qui prend en entrée un KeysetHandle
, ce qui exprime 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 indiquent que pour utiliser cette classe, il doit fournir la description d'une primitive cryptographique.
Rotation des clés
Imaginons un utilisateur Tink qui écrit un programme qui obtient d'abord un ensemble de clés à partir d'un KMS, puis crée un objet AEAD à partir de cet ensemble 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'algorithmes si son choix actuel ne répond plus aux normes.
Il faut toutefois faire preuve de prudence lors de l'implémentation d'une telle rotation de clés : tout d'abord, le KMS doit ajouter une nouvelle clé au jeu de clés (mais ne doit pas encore la définir comme clé principale). Ensuite, le nouveau jeu de clés doit être déployé sur tous les binaires afin que chaque binaire utilisant ce jeu de clés dispose de la clé la plus récente. La nouvelle clé ne doit être définie comme principale que dans ce cas. Le jeu de clés résultant est ensuite à nouveau distribué à tous les binaires qui l'utilisent.
Identifiants de clé dans les textes chiffrés
Reprenons l'exemple d'un ensemble de clés AEAD. Si vous le faites naïvement, le déchiffrement d'un texte chiffré nécessite que Tink tente de le déchiffrer avec toutes les clés du keyset, car il n'est pas possible de savoir quelle clé a été utilisée pour chiffrer le keyset. Cela peut entraîner un coût important en termes de performances.
C'est pourquoi Tink permet de préfixer les textes chiffrés avec une chaîne de 5 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 dérivés de 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 lui doit être utilisé.
Lorsqu'une clé se trouve dans un ensemble de clés, Tink calcule cette balise à partir de l'ID de la clé dans l'ensemble de clés. Le fait que les ID soient uniques2 dans un ensemble de clés implique que les balises sont uniques. Par conséquent, si seules des clés taguées sont utilisées, il n'y a pas de 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 la balise fait partie de la clé, cela implique également que la clé ne peut figurer dans un ensemble de clés que si elle possède un ID spécifique. Cela a des conséquences lorsque vous décrivez l'implémentation des objets clés dans différentes langues.
-
Certaines parties de Tink traitent toujours les ensembles de clés comme un ensemble. Toutefois, cela doit changer. En effet, l'ordre est généralement important: par exemple, considérez le cycle de vie typique d'une rotation de clé avec Aead. Tout d'abord, une nouvelle 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 jeu de clés est déployé sur tous les binaires. Une fois que tous les binaires connaissent la nouvelle clé, elle est définie comme clé principale (à ce stade seulement, l'utilisation de cette clé est sécurisée). Dans cette deuxième étape, la rotation des clés doit connaître la dernière clé ajoutée. ↩
-
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. ↩