Conjuntos de claves

Tink usa conjuntos de claves para habilitar la rotación de claves. De forma formal, un conjunto de claves es una lista1 no vacía de claves en la que una clave se designa como principal (la clave que se usa, por ejemplo, para firmar y encriptar nuevos textos simples). Además, las claves de un conjunto de claves obtienen un ID único2 y un estado de clave que permite inhabilitar claves sin quitarlas de un conjunto de claves.

Los conjuntos de claves son la forma principal en que los usuarios pueden acceder a las claves (a través de la clase KeysetHandle). Esto garantiza que cada usuario tenga código para controlar varias claves a la vez. Para la mayoría de los usuarios de criptografía, controlar varias claves es una necesidad: debe ser posible cambiar las claves (las claves anteriores pueden filtrarse, por ejemplo) y casi nunca hay un "cambio a la siguiente clave" atómico que se pueda aplicar a las máquinas en las que se ejecuta el código y a todos los textos cifrados, de forma global y en un instante. Por lo tanto, el usuario debe escribir código que funcione cuando se cambia de una clave a la siguiente.

Ejemplo: AEAD

Considera un conjunto de claves de AEAD, que contiene varias claves para la primitiva de AEAD. Como se explicó antes, cada clave especifica de forma única dos funciones: \(\mathrm{Enc}\) y \(\mathrm{Dec}\). El conjunto de claves ahora también especifica dos funciones nuevas: \(\mathrm{Enc}\) y \(\mathrm{Dec}\) . \(\mathrm{Enc}\) simplemente es igual a la función \(\mathrm{Enc}\) de la clave principal del conjunto de claves, mientras que la función \(\mathrm{Dec}\) intenta desencriptar con todas las claves, pasando por ellas en algún orden (consulta más abajo para ver cómo Tink mejora el rendimiento de esto).

Es interesante tener en cuenta que los conjuntos de claves son claves completas: son una descripción completa de las funciones \(\mathrm{Enc}\) y\(\mathrm{Dec}\) que se usan. Esto significa que los usuarios pueden escribir una clase que tome como entrada un KeysetHandle, lo que expresa la idea de que la clase necesita una descripción completa de los objetos \(\mathrm{Enc}\) y \(\mathrm{Dec}\) para funcionar correctamente. Esto permite al usuario escribir APIs que comuniquen lo siguiente: Para usar esta clase, debes proporcionarme la descripción de una primitiva criptográfica.

Rotación de claves

Considera un usuario de Tink que escribe un programa que primero obtiene un conjunto de claves de un KMS, luego crea un objeto AEAD a partir de este conjunto de claves y, por último, usa este objeto para encriptar y desencriptar textos cifrados.

Este tipo de usuario está preparado automáticamente para la rotación de claves y para cambiar de algoritmo en caso de que su elección actual ya no cumpla con el estándar.

Sin embargo, se debe tener cuidado cuando se implementa esa rotación de claves: primero, el KMS debe agregar una clave nueva al conjunto de claves (pero aún no debe configurarla como clave principal). Luego, el nuevo conjunto de claves debe implementarse en todos los objetos binarios, de modo que cada uno de ellos tenga la clave más reciente. Solo entonces se debe establecer la clave nueva como principal, y el conjunto de claves resultante se vuelve a distribuir a todos los objetos binarios que lo usan.

Identificadores de claves en textos cifrados

Considera nuevamente el ejemplo de un conjunto de claves AEAD. Si se hace de forma ingenua, la desencriptación de un texto cifrado requiere que Tink intente desencriptar con todas las claves del conjunto de claves, ya que no hay forma de saber qué clave se usó para encriptar el conjunto de claves. Esto puede causar una gran sobrecarga de rendimiento.

Por este motivo, Tink permite anteponer textos cifrados con una cadena de 5 bytes derivada del ID. Siguiendo la filosofía de "Claves completas" anterior, este prefijo es parte de la clave, y todos los textos cifrados que se deriven con esta clave deben tener este prefijo. Cuando los usuarios crean claves, pueden elegir si la clave debe usar ese prefijo o si se debe usar un formato de texto cifrado sin él.

Cuando una clave está en un conjunto de claves, Tink calcula esta etiqueta a partir del ID que tiene la clave en el conjunto de claves. El hecho de que los IDs sean únicos2 dentro de un conjunto de claves implica que las etiquetas también lo son. Por lo tanto, si solo se usan claves etiquetadas, no se pierde el rendimiento en comparación con la desencriptación con una sola clave: Tink solo necesita probar una de las claves cuando desencripta.

Sin embargo, como la etiqueta forma parte de la clave, esto también implica que la clave solo puede estar en un conjunto de claves si tiene un ID específico. Esto tiene algunas implicaciones cuando se describe la implementación de objetos clave en diferentes idiomas.


  1. Algunas partes de Tink aún tratan los conjuntos de claves como un conjunto. Sin embargo, esto debería cambiarse. El motivo es que el orden es, en general, importante: por ejemplo, considera el ciclo de vida típico de una rotación de claves con AEAD. Primero, se agrega una clave nueva a un conjunto de claves. Esta clave aún no es la principal, pero está activa. Este nuevo conjunto de claves se lanzó para todos los objetos binarios. Una vez que todos los objetos binarios conozcan la clave nueva, esta se convertirá en la clave principal (solo en este punto es seguro usarla). En este segundo paso, la rotación de claves debe conocer la última clave agregada. 

  2. Para lograr la compatibilidad con una biblioteca interna de Google, Tink permite tener conjuntos de claves en los que se repiten los IDs. Esta compatibilidad se quitará en el futuro.