Partilhar via


Derivação de subchaves e encriptação autenticada no ASP.NET Core

A maioria das chaves no chaveiro conterá algum tipo de entropia e terá informação algorítmica que indica "encriptação em modo CBC + validação HMAC" ou "encriptação GCM + validação". Nestes casos, referimo-nos à entropia embutida como material de chave mestra (ou KM) para esta chave, e realizamos uma função de derivação de chave para obter 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 a sua própria implementação de IAuthenticatedEncryptor, ao invés de usar uma das nossas fábricas incorporadas, o mecanismo descrito nesta secção deixa de se aplicar.

Dados autenticados adicionais e derivação de subchaves

A IAuthenticatedEncryptor interface serve como interface central para todas as operações de encriptação autenticadas. O seu Encrypt método utiliza dois buffers: texto simples e dados adicionais autenticados (AAD). O conteúdo do texto simples flui sem alterações 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 esta versão do sistema de proteção de dados.

  2. O ID da chave de 128 bits.

  3. Uma cadeia de comprimento variável formada pela cadeia de finalidades que criou a IDataProtector que está a realizar esta operação.

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

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

Aqui, chamamos o NIST SP800-108 KDF em Modo Contador (ver NIST SP800-108, Sec. 5.1) com os seguintes parâmetros:

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

  • PRF = HMACSHA512

  • label = dadosAutenticadosAdicionais

  • contexto = contextHeader || keyModifier

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

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

Encriptação em modo CBC + validação HMAC

Uma vez K_E gerado pelo mecanismo acima, geramos um vetor de inicialização aleatório e executamos o algoritmo de cifra por blocos simétricos para cifrar o texto simples. O vetor de inicialização e o texto cifrado são então executados pela rotina HMAC inicializada com a chave K_H para produzir o MAC. Este processo e o valor de retorno são representados graficamente abaixo.

Processo e retorno em modo CBC

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

Observação

A IDataProtector.Protect implementação irá anteceder o cabeçalho mágico e o ID da chave para a saída antes de o devolver ao chamador. Como o cabeçalho mágico e o id da chave fazem parte implicitamente do AAD, e porque o modificador de chave é fornecido como entrada ao KDF, isto significa que cada byte da carga útil final devolvida é autenticado pelo MAC.

Encriptação em modo Galois/Counter + validação

Uma vez K_E gerado pelo mecanismo acima, geramos um nonce aleatório de 96 bits e executamos o algoritmo de cifra por blocos simétricos para cifrar o texto simples e produzir a etiqueta de autenticação de 128 bits.

Processo e retorno em modo GCM

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

Observação

Apesar de o GCM suportar nativamente o conceito de AAD, continuamos a alimentar AAD apenas ao KDF original, optando por passar uma string vazia para o GCM para o seu parâmetro AAD. A razão para isto é dupla. Primeiro, para suportar a agilidade , nunca queremos usar K_M diretamente como chave de encriptação. Além disso, a GCM impõe requisitos muito rigorosos de unicidade às suas entradas. A probabilidade de a rotina de encriptação GCM alguma vez ser 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 podemos realizar mais do que 2^32 operações de encriptação antes de infringirmos o limite de 2^-32. Isto pode parecer um número muito elevado de operações, mas um servidor web de alto tráfego pode processar 4 mil milhões de pedidos em poucos dias, bem dentro do tempo de vida normal destas 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 nonce de 96 bits, o que estende radicalmente a contagem de operações utilizáveis para qualquer dado K_M. Para simplificar o design, partilhamos 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 GCM.