Derivação de subchave e criptografia autenticada no ASP.NET Core

A maioria das chaves no chaveiro conterá alguma forma de entropia e terá informações algorítmicas informando "Criptografia no modo CBC + validação HMAC" ou "Criptografia GCM + validação". Nesses casos, nos referimos à entropia inserida como o material do chaveiro mestre (ou KM) para essa chave e executamos uma função de derivação de chave para derivar as chaves que serão usadas para as operações criptográficas reais.

Observação

As chaves são abstratas e uma implementação personalizada pode não se comportar como abaixo. Se a chave fornecer sua própria implementação de IAuthenticatedEncryptor em vez de usar uma de nossas fábricas internas, o mecanismo descrito nesta seção não se aplicará mais.

Derivação de subchave e dados autenticados adicionais

A interface IAuthenticatedEncryptor serve como a interface principal para todas as operações de criptografia autenticadas. Seu método Encrypt usa dois buffers: plaintext e additionalAuthenticatedData (AAD). O conteúdo de texto sem formatação flui inalterado na chamada para IDataProtector.Protect, mas o AAD é gerado pelo sistema e consiste em três componentes:

  1. O cabeçalho mágico de 32 bits 09 F0 C9 F0 que identifica essa versão do sistema de proteção de dados.

  2. A ID da chave de 128 bits.

  3. Uma cadeia de caracteres de comprimento variável formada a partir da cadeia de finalidade que criou o IDataProtector que está executando essa operação.

Como o AAD é exclusivo para a tupla dos três componentes, podemos usá-lo para derivar novas chaves do KM em vez de usar o próprio KM em todas as nossas operações criptográficas. Para cada chamada a IAuthenticatedEncryptor.Encrypt, o seguinte processo de derivação de chave ocorre:

( K_E, K_H ) = SP800_108_CTR_HMACSHA512(K_M, AAD, contextHeader || keyModifier)

Aqui, estamos chamando o NIST SP800-108 KDF no Modo de Contador (consulte NIST SP800-108, s. 5.1) com os seguintes parâmetros:

  • Chave de derivação de chave (KDK) = K_M

  • PRF = HMACSHA512

  • label = additionalAuthenticatedData

  • context = contextHeader || keyModifier

O cabeçalho de contexto é de comprimento variável e essencialmente serve como uma impressão digital dos algoritmos para os quais estamos derivando K_E e K_H. O modificador de chave é uma cadeia de caracteres de 128 bits gerada aleatoriamente para cada chamada a Encrypt e serve para garantir, com uma probabilidade esmagadora, que KE e KH sejam exclusivos para essa operação de criptografia de autenticação específica, mesmo que todas as outras entradas para o KDF sejam constantes.

Para operações de validação de criptografia no modo CBC + HMAC, | K_E | é o comprimento da chave de criptografia de bloco simétrico e | K_H | é o tamanho do resumo da rotina HMAC. Para operações de criptografia de GCM + validação, | K_H | = 0.

Criptografia no modo CBC + validação de HMAC

Depois que K_E for gerado por meio do mecanismo acima, geramos um vetor de inicialização aleatório e executamos o algoritmo de codificação de bloco simétrico para cercar o texto sem formatação. O vetor de inicialização e o texto cifrado são executados por meio da rotina HMAC inicializada com a chave K_H para produzir o MAC. Esse processo e o valor retornado são representados graficamente abaixo.

CBC-mode process and return

output:= keyModifier || iv || E_cbc (K_E,iv,data) || HMAC(K_H, iv || E_cbc (K_E,iv,data))

Observação

A implementaçãoIDataProtector.Protectanexará o cabeçalho mágico e a ID da chave à saída antes de devolvê-la ao chamador. Como o cabeçalho mágico e a ID da chave fazem parte implicitamente do AAD e, como o modificador de chave é alimentado como entrada para o KDF, isso significa que cada byte da carga final retornada é autenticado pelo MAC.

Criptografia e validação do Galois/Modo de Contador

Depois que K_E for gerado por meio do mecanismo acima, geramos um nó aleatório de 96 bits e executamos o algoritmo de codificação de bloco simétrico para cercar o texto sem formatação e produzir a marca de autenticação de 128 bits.

GCM-mode process and return

output := keyModifier || nonce || E_gcm (K_E,nonce,data) || authTag

Observação

Embora o GCM dê suporte nativo ao conceito de AAD, ainda estamos alimentando o AAD apenas para o KDF original, optando por passar uma cadeia de caracteres vazia para o GCM para seu parâmetro AAD. Existem duas razões para isso: Primeiro, para dar suporte à agilidade, nunca devemos usar K_M diretamente como a chave de criptografia. Além disso, o GCM impõe requisitos de exclusividade muito rigorosos em suas entradas. A probabilidade de que a rotina de criptografia GCM seja invocada em dois ou mais conjuntos distintos de dados de entrada com o mesmo par (chave, nonce) não deve exceder 2^-32. Se corrigirmos K_E, não poderemos executar mais de 2^32 operações de criptografia antes de executarmos o limite de 2^-32. Isso pode parecer um número muito grande de operações, mas um servidor Web de alto tráfego pode passar por 4 bilhões de solicitações em apenas alguns dias, bem dentro do tempo de vida normal dessas chaves. Para manter a conformidade com o limite de probabilidade de 2^-32, continuamos a usar um modificador de chave de 128 bits e um nó de 96 bits, o que estende radicalmente a contagem de operações utilizável para qualquer K_M determinado. Para simplificar o design, compartilhamos o caminho do código KDF entre as operações CBC e GCM e, como o AAD já é considerado no KDF, não é necessário encaminhá-lo para a rotina do GCM.