Tink usa conjuntos de claves para habilitar la rotación de claves. Formalmente, 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 textos sin formato nuevos). 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 principal forma 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, el manejo de varias claves es una necesidad: debe ser posible cambiar las claves (por ejemplo, las claves antiguas se pueden filtrar) 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 AEAD, que contiene varias claves para la primitiva 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 equivale 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, recorriéndolas en algún orden (consulta a continuación para saber cómo Tink mejora el rendimiento de esto).
Es interesante observar que los conjuntos de claves son claves completas: son una descripción completa de las funciones \(\mathrm{Enc}\) y\(\mathrm{Dec}\) utilizadas. Esto significa que los usuarios pueden escribir una clase que tome un KeysetHandle como entrada, 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 que el usuario escriba APIs que comuniquen lo siguiente: Para usar esta clase, debes proporcionarme la descripción de un elemento criptográfico primitivo.
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, finalmente, usa este objeto para encriptar y desencriptar textos cifrados.
Este usuario se prepara automáticamente para la rotación de claves y el cambio de algoritmos en caso de que su elección actual ya no cumpla con el estándar.
Sin embargo, se debe tener cierto cuidado al implementar la rotación de claves: Primero, el KMS debe agregar una clave nueva al conjunto de claves (pero aún no establecerla como primaria). Luego, el nuevo conjunto de claves debe implementarse en todos los archivos binarios para que cada uno de ellos que use este conjunto 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 archivos binarios que usan el conjunto de claves.
Identificadores clave en textos cifrados
Considera nuevamente el ejemplo de un conjunto de claves AEAD. Si se hace de forma ingenua, desencriptar 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 a los textos cifrados una cadena de 5 bytes derivada del ID. Siguiendo la filosofía de "Claves completas" anterior, este prefijo forma 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 son únicas. Por lo tanto, si solo se usan claves etiquetadas, no hay pérdida de rendimiento en comparación con el descifrado con una sola clave: Tink solo necesita probar una de las claves cuando descifra.
Sin embargo, dado que 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.
Claves con un requisito de ID, pero sin prefijo de resultado
Algunas claves deben tener un ID específico, pero no agregan ningún prefijo a su salida. Por ejemplo, las claves de firma con la variante NO_PREFIX_WITH_PREHASH_ID (almacenadas con el tipo de prefijo de salida WITH_ID_REQUIREMENT) producen firmas sin prefijo. Cuando usas una clave de este tipo con la primitiva Prehash, Tink escribe el ID de la clave en el valor de prehash, de modo que un firmante remoto sepa con cuál de sus claves firmar.
Al igual que con las claves que usan un prefijo, una clave de este tipo solo puede estar en un conjunto de claves con ese ID. El ID de clave en el valor previo al hash son metadatos simples, al igual que el prefijo de resultado: la firma no lo enlaza y los verificadores nunca lo ven. Para el diseño a nivel de bytes, consulta Formato de transferencia de Tink.
-
Algunas partes de Tink aún tratan a los Keysets como un conjunto. Sin embargo, esto debería cambiar. El motivo es que el orden suele ser 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 principal, pero está activa. Este nuevo conjunto de claves se lanza para todos los archivos binarios. Una vez que todos los archivos binarios conocen la clave nueva, esta se convierte en la clave principal (solo en este punto es seguro usar esta clave). En este segundo paso, la rotación de claves debe conocer la última clave agregada. ↩
-
Para 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. ↩