Primitives et interfaces

Nous définissons ensuite (informément, mais plus formellement) deux éléments importants du langage utilisé dans Tink : le Primitive (Primitif) et l'Interface (Interface).

Primitif

Une primitive est un objet mathématique correspondant à tous les algorithmes exécutant une tâche de manière sécurisée. Par exemple, la primitive AEAD est composée de tous les algorithmes de chiffrement qui satisfont aux propriétés de sécurité requises par Tink pour un Aead.

Nous soulignons que les primitives ne sont pas liées à un langage de programmation ni à un moyen spécifique d'y accéder. Il faut considérer le primitif comme l'objet purement mathématique. Par exemple, si nous considérons le chiffrement AEAD, il est fondamentalement constitué de paires de fonctions, l'une qui effectue le chiffrement et l'autre qui effectue le déchiffrement.

Interfaces

Une interface est un moyen par lequel nous fournissons aux utilisateurs l'accès à une primitive. Par exemple, nous nous attendons à ce qu'à l'avenir Tink fournira une interface Mac, mais également une interface StreamingMac, qui permet de calculer le MAC de données qui ne sont pas directement chargées en mémoire.

Notez que nous distinguons ici explicitement les interfaces et les primitives. Cela devrait indiquer clairement que l'objet mathématique auquel ces deux interfaces donnent accès sont le même.

Définitions formelles

Pour la plupart des lecteurs, les explications intuitives ci-dessus devraient suffire. Néanmoins, nous pensons qu'il peut parfois être important de fournir des définitions formelles de ces concepts.

Fonctions cryptographiques

Le concept de fonction cryptographique n'est pas aussi important que celui de primitive, mais nous devons l'introduire pour la définir formellement.

Fonction cryptographique

Une fonction cryptographique est une carte

\[ f: {\bf K} \times {\bf R} \times {\bf I} \to {\bf O}\]

d'un ensemble \({\bf K}\) (espace clé), d'un ensemble \({\bf R} = \{0,1\}^{\infty}\)(aléatoire, que nous supposons être l'ensemble de chaînes de bits infinies) et d'un ensemble \({\bf I}\) (espace d'entrée), à un ensemble \({\bf O}\) (l'espace de sortie).

Nous verrons plus tard pourquoi nous avons ajouté un paramètre de hasard spécifique.

À titre d'exemple, nous montrons comment mapper ces concepts à AES-GCM. Pour chaque taille de clé \(s_k\), taille de nonce \(s_n\)et taille de tag\(s_t\)valides, AES-GCM comprend deux fonctions de chiffrement, l'une pour le chiffrement et l'autre pour le déchiffrement. Les deux auront le même espace clé \({\bf K} = \{0,1\}^{s_k}\).

Pour la fonction de chiffrement \(\mathrm{Enc}\), les \(s_n\) premiers bits de hasard sont utilisés pour sélectionner le nonce.

\({\bf B} = \{0,1\}^8\) désigne un octet. L'espace d'entrée de la fonction de chiffrement correspond aux paires \({\bf I} = {\bf B}^{*} \times {\bf B}^{*}\) de paires de chaînes d'octets de longueur arbitraire. Le premier élément de la paire est destiné au message, et le second élément aux données associées. La norme AES-GCM a une limite supérieure pour la longueur des entrées, mais nous préférons autoriser des longueurs arbitraires et ajouter à la place un symbole d'erreur spécial \(\bot\) à l'espace de sortie. L'espace de sortie devient alors \({\bf O} = {\bf B}^* \cup \{\bot\}\), où nous définissons arbitrairement le résultat de calculs réussis comme la concaténation \((\mathrm{IV} \| \mathrm{ciphertext} \| \mathrm{tag})\) fournie dans la norme, et la sortie\(\bot\), au cas où une entrée serait trop longue. Par conséquent, pour une clé fixe, la fonction de chiffrement devient \(\mathrm{Enc}_k : {\bf R} \times {\bf B}^* \times {\bf B}^* \rightarrow {\bf B}^* \cup \{\bot\}\).

Pour la fonction de déchiffrement \(\mathrm{Dec}\) , l'espace clé est le même. L'espace d'entrée par coïncidence est le même: \({\bf I} ={\bf B}^* \times {\bf B}^*\), mais le premier élément est désormais destiné à être la sortie de la fonction de chiffrement, tandis que le second est toujours les données associées.

L'espace de sortie est également le même \({\bf O} = {\bf B}^* \cup \{\bot\}\) (encore une coïncidence). L'interprétation est légèrement différente, car \(\bot\) indique généralement une erreur d'authentification (bien que ce soit également la sortie si une entrée est trop longue).

Nous soulignons que la formalisation ci-dessus n'est pas la seule option pour formaliser la norme. Par exemple, on pourrait considérer que le nonce fait partie de l'entrée, au lieu de le lire à partir du hasard (ce qui donne une primitive très différente). Vous pouvez également définir la sortie comme un triple contenant le nonce, le texte chiffré et la balise (au lieu de la concaténation). Vous pouvez également restreindre l'espace de clés (de manière arbitraire) à\({\bf K} = \{0,1\}^{128} \cup \{0,1\}^{256}\).

Algorithme cryptographique:

Un algorithme cryptographique (symétrique) est un tuple.

\[(f_1, ... f_k)\]

des fonctions cryptographiques, où toutes les fonctions ont le même espace clé. Le type de l'algorithme cryptographique correspond au tuple \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\).

Par exemple, pour chaque triple \((s_k, s_n, s_t)\) de taille de clé, de nonce et de balise, AES-GCM\({}_{s_k, s_n, s_t}\) est un algorithme de chiffrement doté des deux fonctions \(\mathrm{Enc}\) et \(\mathrm{Dec}\) décrites ci-dessus.

Primitives et interfaces

Nous définissons ensuite une primitive cryptographique.

Primitif
Une primitive est un ensemble d'algorithmes cryptographiques dans lesquels tous les algorithmes ont le même type \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\), et dont les espaces clés sont disjoints par paire.

Prenons l'exemple de la primitive \(\mathrm{AEAD}\) dans Tink. Il dispose de plusieurs algorithmes, dont AES-GCM pour les tailles de clé de 128 et 256 bits, avec une taille de nonce de 96 bits, AES-EAX avec certaines tailles de clé et XChaCha20Poly1305. Elles comportent des espaces de clés disjoints, mais fournissent toutes les mêmes fonctions de chiffrement\(\mathrm{Enc}\) et \(\mathrm{Dec}\). (Nous ne voyons pas dans cette discussion formelle l'objectif de réduire les différentes tailles de clé d'AES-GCM, mais bien sûr, cela pourrait être le cas.)

Définir des primitives

La façon habituelle d'envisager les primitives consiste à définir d'abord les propriétés des fonctions cryptographiques, puis à considérer les primitives comme de tels algorithmes.

Par exemple, pour AEAD, nous indiquons que \(\mathrm{Dec}_k(\mathrm{Enc}_k(m, a), a) = m\) est "toujours" satisfait (sauf si le texte brut \(m\) est trop long, par exemple). Nous possédons également des propriétés de sécurité. Par exemple, pour une clé aléatoire, le chiffrement est sensiblement sécurisé.

La primitive AEAD correspond alors simplement à l'ensemble de tous les algorithmes cryptographiques répondant à ces propriétés. En d'autres termes, en pratique, lorsque nous définissons une primitive spécifique, nous la définissons en fonction de propriétés. Nous ne fournissons pas de liste d'algorithmes, comme l'indique la définition.

Interfaces

Une interface dans Tink donne accès à une primitive, dans le sens où elle permet de calculer un élément de l'espace de sortie à partir de l'espace d'entrée. Prenons l'exemple de l'interface AEAD en Java:

public interface Aead {
  byte[] encrypt(byte[] plaintext, byte[] associated_data) throws GeneralSecurityException;
  byte[] decrypt(byte[] ciphertext, byte[] associated_data) throws GeneralSecurityException;
}

Notez que nous ne donnons pas accès au hasard. Au lieu de cela, nous permettons à l'utilisateur de fournir des éléments de l'espace d'entrée. Interdire l'accès au hasard est bien sûr intentionnel1.

Tink propose parfois plusieurs interfaces pour une même primitive. Cela peut être très utile, car les exigences diffèrent parfois. Cependant, cela a un prix: en général, plus on offre d'interfaces, moins l'interopérabilité est faible. Par exemple, imaginez que quelqu'un écrit une bibliothèque basée sur Tink qui exige que l'utilisateur transmet un objet Aead (pour chiffrer un élément en interne). Si Tink propose trop d'interfaces différentes à la primitive \(\mathrm{AEAD}\) , il est fort probable que l'utilisateur ne dispose pas d'une instance prête pour la clé choisie par l'utilisateur et pour la bibliothèque en même temps. Par conséquent, l'ajout d'interfaces est un compromis.


  1. Les chiffrements AEAD ont la propriété d'être sécurisés contre les attaques à texte chiffré choisi, ce qui n'est garanti que si le nonce n'est pas réutilisé. L'interface Aead de Tink est conçue de manière à empêcher la réutilisation des nonce: l'utilisateur ne peut pas fournir de nonce comme entrée pour le chiffrement. À la place, un nouveau nonce est généré de manière aléatoire pour chaque opération de chiffrement.