Interfaces y primitivas

A continuación, definimos (informalmente, pero más formalmente) dos partes importantes de el lenguaje que se usa en Tink, Primitive y la Interface.

Básico

Un primitivo es un objeto matemático que corresponde a todos los algoritmos realizar alguna tarea de forma segura. Por ejemplo, la primitiva AEAD consta de todos de encriptación que satisfacen las propiedades de seguridad que Tink requiere de una Aead.

Hacemos hincapié en que las primitivas no están vinculadas a un lenguaje de programación ni a un lenguaje forma de antes de acceder a ellos. En cambio, hay que pensar en lo primitivo como como un objeto matemático. Por ejemplo, si consideramos AEAD, fundamentalmente constan de pares de funciones, una que realiza la encriptación y otra que realiza la desencriptación.

Interfaces

Una interfaz es una forma en la que proporcionamos a los usuarios acceso a un primitivo. Por ejemplo, esperamos que, en el futuro, Tink proporcione una interfaz Mac pero también una interfaz StreamingMac, que permite procesar la Mac de que no se cargan directamente en la memoria.

Ten en cuenta que aquí se distinguen explícitamente interfaces y primitivas. Esto debería aclarar que el objeto matemático al que estas dos interfaces le dan de acceso son los mismos.

Definiciones formales

Para la mayoría de los lectores, las explicaciones intuitivas anteriores probablemente sean suficientes. Sin embargo, creemos que a veces puede ser importante brindar de estos conceptos.

Funciones criptográficas

El concepto de función criptográfica no es tan importante como el concepto de una pero debemos introducirlo para definirlo formalmente.

Función criptográfica

Una función criptográfica es un mapa.

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

de un conjunto \({\bf K}\) (el espacio de claves), un conjunto \({\bf R} = \{0,1\}^{\infty}\) (aleatorización, que suponemos que es el conjunto de cadenas de bits infinitas) y un set \({\bf I}\) (el espacio de entrada), a un conjunto \({\bf O}\) (el espacio de salida).

Más adelante quedará claro por qué agregamos un parámetro de aleatorización específico.

A modo de ejemplo, mostramos una posibilidad de cómo se pueden asignar estos conceptos a AES-GCM Para cada tamaño de clave válido \(s_k\), tamaño del nonce \(s_n\)y tamaño de etiqueta \(s_t\), AES-GCM tiene dos funciones criptográficas, una para encriptación y otra para desencriptación. Ambos tendrán el mismo espacio de claves \({\bf K} = \{0,1\}^{s_k}\).

Para la función de encriptación \(\mathrm{Enc}\), los primeros \(s_n\) bits de para seleccionar el nonce.

Deja que \({\bf B} = \{0,1\}^8\) denote un byte. El espacio de entrada de la función de encriptación es el par \({\bf I} = {\bf B}^{*} \times {\bf B}^{*}\) de pares de strings de bytes de longitud arbitraria. El primer elemento del par debe ser el mensaje; el segundo los datos asociados. El estándar AES-GCM tiene un límite superior para las longitudes de las entradas, pero preferimos permitir longitudes arbitrarias y, en su lugar, agregamos una función especial símbolo de error \(\bot\) al espacio de salida. El espacio de salida se convierte en \({\bf O} = {\bf B}^* \cup \{\bot\}\), donde definimos de manera arbitraria el resultado de cálculos exitosos a través de la concatenación \((\mathrm{IV} \| \mathrm{ciphertext} \| \mathrm{tag})\) como se indica en el estándar, y la salida \(\bot\), en caso de que alguna entrada sea demasiado larga. Por lo tanto, para una clave fija, la la función de encriptación se convierte en el tipo \(\mathrm{Enc}_k : {\bf R} \times {\bf B}^* \times {\bf B}^* \rightarrow {\bf B}^* \cup \{\bot\}\).

Para la función de desencriptación, \(\mathrm{Dec}\) el espacio de claves es el mismo. El casualmente el espacio de entrada es el mismo: \({\bf I} ={\bf B}^* \times {\bf B}^*\), pero ahora el primer elemento debe ser el resultado de la función de encriptación mientras que el segundo sigue siendo los datos asociados.

El espacio de salida también es el mismo \({\bf O} = {\bf B}^* \cup \{\bot\}\) (de nuevo, una coincidencia). La interpretación es algo diferente, como \(\bot\) generalmente denota un error de autenticación (aunque también será el salida en caso de que alguna entrada sea demasiado larga).

Destacamos que la formalización anterior no es la única opción para formalizar estándar. Por ejemplo, podríamos considerar que el nonce es una parte de la entrada, en lugar de leerla de la aleatoriedad (lo que da como resultado una primitiva muy diferente). De manera alternativa, se podría definir la salida como un triple que contiene el nonce. el texto cifrado y la etiqueta (en lugar de la concatenación). O podría restringir el espacio de claves (de forma arbitraria) a \({\bf K} = \{0,1\}^{128} \cup \{0,1\}^{256}\)

Algoritmo criptográfico:

Un algoritmo criptográfico (simétrico) es una tupla.

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

de funciones criptográficas, en el que todas las funciones tienen el mismo espacio de claves. El El tipo del algoritmo criptográfico es la tupla \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\).

Por ejemplo, por cada triple \((s_k, s_n, s_t)\) de clave, nonce y etiqueta válidos AES-GCM\({}_{s_k, s_n, s_t}\) es un algoritmo criptográfico con el dos funciones \(\mathrm{Enc}\) y \(\mathrm{Dec}\) las descritas anteriormente.

Interfaces y primitivas

A continuación, definiremos un primitivo criptográfica.

Básico
Un primitivo es un conjunto de algoritmos criptográficos en el que todos los algoritmos tienen el mismo tipo \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\), y los espacios clave de los algoritmos son inconexos en pares.

Como ejemplo, considera la primitiva \(\mathrm{AEAD}\) en Tink. Tiene varias como AES-GCM para claves de 128 y 256 bits, con nonce de 96 bits, AES-EAX con algunos tamaños de clave y XChaCha20Poly1305. Tienen espacios de claves inconexos, pero que proporcionan las mismas funciones criptográficas \(\mathrm{Enc}\) y \(\mathrm{Dec}\). (No vemos un propósito de alguna manera diferentes tamaños de claves de AES-GCM en este debate formal, pero por supuesto que podríamos hacerlo).

Cómo definir primitivas

La forma habitual de pensar en los primitivos es definir primero las propiedades del funciones criptográficas y, luego, simplemente considerar que la primitiva es todo este tipo de algoritmos.

Por ejemplo, para AEAD, diría que \(\mathrm{Dec}_k(\mathrm{Enc}_k(m, a), a) = m\) es "siempre". satisfecho (excepto si el texto simple \(m\) es demasiado de larga duración). Además, tenemos propiedades de seguridad: por ejemplo, para una clave aleatoria, la encriptación es segura de forma semántica.

La primitiva AEAD es simplemente el conjunto de todos los algoritmos criptográficos cumplan con estas propiedades. En otras palabras, en la práctica, cuando definimos un tipo primitiva, la definimos en función de las propiedades. No proporcionamos una lista de con algoritmos criptográficos, como sugiere la definición.

Interfaces

Una interfaz en Tink otorga acceso a un primitivo, en el sentido que permite para procesar un elemento del espacio de salida desde el espacio de entrada. Por ejemplo: considera la interfaz AEAD en Java:

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

Ten en cuenta que no otorgamos acceso a la aleatoriedad. En cambio, permitimos que el usuario proporcionar elementos del espacio de entrada. No permitir el acceso a la aleatoriedad es de curso a propósito.1

A veces, Tink ofrece varias interfaces para una sola primitiva. Esto puede ser muy útil, ya que los requisitos a veces difieren. No obstante, hacer esto tiene un precio: en general, cuantas más interfaces ofrezca, menor la interoperabilidad. Por ejemplo, imagina que alguien escribe una biblioteca basada en Tink que requiere que el usuario pase un Aead (para encriptar contenido de forma interna). Si Tink ofrece demasiadas interfaces diferentes para la primitiva \(\mathrm{AEAD}\) , hay muchas posibilidades que el usuario no tiene una instancia lista que funcione para la clave que eligió el usuario y la biblioteca al mismo tiempo. Por lo tanto, agregar más interfaces es una desventaja.


  1. Los cifrados AEAD tienen la propiedad de que son seguros contra ataques de texto cifrado elegidos, lo que se garantiza solo si no hay la reutilización del nonce. La interfaz Aead de Tink está diseñada de tal manera que evita la reutilización de nonce: el usuario no puede proporcionar un nonce como entrada para la encriptación. en su lugar, se genera de forma aleatoria un nonce nuevo para cada operación de encriptación.