Conjunto de chaves

O 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 chave é designada como primária (a chave usada, por exemplo, para assinar e criptografar novos textos simples). Além disso, as chaves em um conjunto de chaves recebem um ID exclusivo2 e um status que permite desativar chaves sem removê-las de um conjunto de chaves.

Os conjuntos de chaves são a principal maneira de os usuários acessarem chaves (pela classe KeysetHandle). Isso garante que todos os usuários tenham código para processar várias chaves de uma vez. Para a maioria dos usuários de criptografia, o processamento de várias chaves é uma necessidade: é necessário que seja possível mudar as chaves (chaves antigas podem ser vazadas, por exemplo), e quase nunca há uma "mudança para a próxima chave" atômica que possa ser aplicada às máquinas em que o código é executado e a todos os textos criptografados, globalmente e em um instante. Portanto, o usuário precisa escrever um código que funcione quando uma tecla muda para a próxima.

Exemplo: AEAD

Considere um conjunto de chaves AEAD, que contém várias chaves para a primitiva AEAD. Como explicado anteriormente, cada chave especifica duas funções: \(\mathrm{Enc}\) e \(\mathrm{Dec}\). O conjunto de chaves agora também especifica duas novas funções: \(\mathrm{Enc}\) e \(\mathrm{Dec}\) - \(\mathrm{Enc}\) simplesmente são iguais à 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 para saber como o Tink melhora o desempenho desse processo).

É interessante notar que os conjuntos de chaves são chaves completas: são uma descrição completa das funções \(\mathrm{Enc}\) e \(\mathrm{Dec}\) usadas. Isso significa que os usuários podem escrever uma classe que recebe como entrada um KeysetHandle, expressando a ideia de que a classe precisa de uma descrição completa de objetos \(\mathrm{Enc}\) e \(\mathrm{Dec}\) para funcionar corretamente. Isso permite que o usuário escreva APIs que comuniquem que, para usar essa classe, você precisa fornecer a descrição de uma primitiva criptográfica.

Rotação de chaves

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

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

No entanto, é preciso ter cuidado ao implementar essa rotação de chaves: primeiro, o KMS precisa adicionar uma nova chave ao conjunto de chaves (mas não defini-la como primária). Em seguida, o novo conjunto de chaves precisa ser lançado para todos os binários, para que cada binário que use esse conjunto tenha a chave mais recente no conjunto. Só então a nova chave será considerada primária, e o conjunto de chaves resultante será distribuído novamente para todos os binários que usam o conjunto de chaves.

Identificadores de chaves em textos criptografados

Considere novamente o exemplo de um conjunto de chaves AEAD. Se feito de forma simples, a descriptografia de um texto criptografado exige 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. Isso pode causar uma sobrecarga de desempenho.

Por isso, o Tink permite prefixar textos criptografados com uma string de 5 bytes derivada do ID. Seguindo a filosofia de "Chaves completas" acima, esse prefixo é parte da chave, e todos os textos criptografados derivados dela precisam ter esse prefixo. Quando os usuários criam chaves, eles 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 com base no ID que a chave tem no conjunto. O fato de os IDs serem exclusivos2 em um conjunto de chaves implica que as tags também são exclusivas. Portanto, se apenas chaves marcadas forem usadas, não haverá perda de desempenho em comparação com a descriptografia com uma única chave: o Tink só precisa tentar uma das chaves ao descriptografar.

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 principais em diferentes idiomas.


  1. Algumas partes do Tink ainda tratam os Keysets como um conjunto. No entanto, isso precisa ser alterado. O motivo é que a ordem é importante de modo geral: por exemplo, considere o ciclo de vida típico de uma rotação de chaves com Aead. Primeiro, uma nova chave é adicionada a um conjunto de chaves. Essa chave ainda não é primária, mas está ativa. Esse novo conjunto de chaves foi lançado para todos os binários. Quando todos os binários conhecem a nova chave, ela se torna primária (apenas neste ponto o uso dessa chave é seguro). Nesta segunda etapa, a rotação de chaves precisa saber qual foi 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.