Conjunto de chaves

A Tink usa conjuntos de chaves para ativar a rotação de chaves. Formalmente, um conjunto de chaves é uma lista não vazia1 de chaves em que uma é designada como principal (a chave usada, por exemplo, para assinar e criptografar novos textos simples). Além disso, as chaves em um conjunto de chaves recebem um ID2 exclusivo e um status que permite desativar as chaves sem removê-las de um conjunto.

Conjuntos de chaves são a principal maneira pela qual os usuários podem acessar chaves (por meio da classe KeysetHandle). Isso garante que cada usuário tenha um código para lidar com várias chaves ao mesmo tempo. Para a maioria dos usuários de criptografia, processar várias chaves é uma necessidade: é preciso ser possível mudar chaves (por exemplo, chaves antigas podem ser vazadas), e quase nunca há uma "alternar para a próxima chave" atômica que possa ser aplicada às máquinas que o código executa e a todos os textos criptografados, globalmente e em um instante. Portanto, o usuário precisa escrever um código que funciona quando uma chave muda para a próxima.

Exemplo: AEAD

Considere um conjunto de chaves AEAD, que contém várias chaves para o primitivo AEAD. Como explicado anteriormente, cada chave especifica de forma exclusiva duas funções: \(\mathrm{Enc}\) e \(\mathrm{Dec}\). O conjunto de chaves agora especifica duas novas funções: \(\mathrm{Enc}\) e \(\mathrm{Dec}\) - \(\mathrm{Enc}\) simplesmente é igual à função \(\mathrm{Enc}\) da chave primária do conjunto de chaves, enquanto a função \(\mathrm{Dec}\) tenta descriptografar com todas as chaves, passando por elas em alguma ordem. Consulte abaixo como a Tink melhora o desempenho dessa parte.

É interessante observar que os conjuntos de chaves são chaves completas: elas são uma descrição completa das funções \(\mathrm{Enc}\) e \(\mathrm{Dec}\) usadas. Isso significa que os usuários podem criar uma classe que usa como entrada um KeysetHandle, expressando a ideia de que a classe precisa de uma descrição completa dos objetos \(\mathrm{Enc}\) e \(\mathrm{Dec}\) para funcionar corretamente. Isso permite que o usuário programe APIs que comunicam que: para usar essa classe, você precisa me fornecer a descrição de um primitivo criptográfico.

Rotação de chaves

Considere um usuário do Tink, criando um programa que primeiro recebe um conjunto de chaves de um KMS, depois cria um objeto AEAD desse conjunto e, por fim, usa esse objeto para criptografar e descriptografar textos.

Esse usuário fica automaticamente preparado para a rotação de chaves e troca de algoritmos caso a escolha atual não atenda mais ao padrão.

No entanto, é preciso ter um pouco de cuidado ao implementar essa rotação de chaves: primeiro, o KMS precisa adicionar uma nova chave ao conjunto de chaves (mas ainda não defini-la como primária). Em seguida, o novo conjunto de chaves precisa ser implantado para todos os binários, para que cada um deles tenha a chave mais recente nele. Só então a nova chave vai se tornar a principal, e o conjunto de chaves resultante será distribuído novamente para todos os binários que usam esse conjunto.

Identificadores de chaves em textos criptografados

Considere novamente o exemplo de um conjunto de chaves AEAD. Se feita de forma ingênua, a descriptografia de um texto criptografado vai exigir que o Tink tente descriptografar com todas as chaves do conjunto de chaves, já que não há como saber qual chave foi usada para criptografar o conjunto de chaves. Isso pode causar um grande overhead de desempenho.

Por isso, o Tink permite prefixar textos criptografados com uma string de cinco bytes derivada do ID. Seguindo a filosofia de "chaves completas" acima, esse prefixo é parte da chave, e todos os textos criptografados já derivados com essa chave precisam ter esse prefixo. Ao criar chaves, os usuários podem escolher se a chave vai usar esse prefixo ou se um formato de texto criptografado sem ele será usado.

Quando uma chave está em um conjunto de chaves, o Tink calcula essa tag a partir do ID que a chave tem no conjunto de chaves. O fato de os IDs serem exclusivos2 em um conjunto de chaves implica que as tags são exclusivas. Portanto, se apenas chaves com tags forem usadas, não haverá perda de desempenho em comparação com a descriptografia com uma única chave: a Tink só precisa testar uma das chaves durante a descriptografia.

No entanto, como a tag faz parte da chave, isso também implica que a chave só pode estar em um conjunto de chaves se tiver um ID específico. Isso tem algumas implicações ao descrever a implementação de objetos de chave em diferentes linguagens.


  1. Algumas partes do Tink ainda tratam os conjuntos de chaves como um conjunto. No entanto, isso precisa ser alterado. O motivo é que a ordem é, em geral, importante. Por exemplo, considere o ciclo de vida típico de uma rotação de chaves com o Aead. Primeiro, uma nova chave é adicionada a um conjunto. Essa chave ainda não é a principal, mas está ativa. Esse novo conjunto de chaves é implantado para todos os binários. Depois que todos os binários conhecem a nova chave, ela se torna primária. Somente nesse momento, usar essa chave é seguro. Nessa segunda etapa, a rotação de chaves precisa saber a última chave adicionada. 

  2. Para compatibilidade com uma biblioteca interna do Google, o Tink permite ter conjuntos de chaves em que os IDs são repetidos. Esse suporte será removido no futuro.