A seguir, definimos (informalmente, mas, de forma mais formal), duas partes importantes a linguagem usada no Tink, no Primitive e na Interface;
Primário
Um primitivo é um objeto matemático que corresponde a todos os algoritmos para realizar uma tarefa com segurança. Por exemplo, o primitivo da AEAD consiste em todas algoritmos de criptografia que satisfazem as propriedades de segurança exigidas pelo Tink de um Aead.
Enfatizamos que os primitivos não estão vinculados a uma linguagem de programação maneira de para acessá-las. Em vez disso, devemos pensar no primitivo como o um objeto matemático. Por exemplo, se considerarmos o AEAD, fundamentalmente ele consistem em pares de funções, uma que realiza a criptografia e a outra executa a descriptografia.
Interfaces
Uma interface é uma forma de fornecer aos usuários acesso a um primitivo.
Por exemplo, esperamos que, no futuro, o Tink forneça uma interface Mac
,
mas também uma interface StreamingMac
, que permite computar o MAC do
dados que não são diretamente carregados na memória.
Aqui, diferenciamos explicitamente interfaces e primitivos. Isso deve deixa claro que o objeto matemático para o qual essas duas interfaces dão e acesso são os mesmos.
Definições formais
Para a maioria dos leitores, as explicações intuitivas acima provavelmente são suficientes. No entanto, achamos que pode ser importante, às vezes, fornecer informações e definições desses conceitos.
Funções criptográficas
O conceito de uma função criptográfica não é tão importante quanto o de uma primitivo, mas precisamos introduzi-lo para definir formalmente o primitivo.
- Função criptográfica
Uma função criptográfica é um mapa.
\[ f: {\bf K} \times {\bf R} \times {\bf I} \to {\bf O}\]
de um conjunto \({\bf K}\) (o espaço da chave), um conjunto \({\bf R} = \{0,1\}^{\infty}\) (aleatoriedade, que presumimos ser o conjunto de bitstrings infinitas) e uma set \({\bf I}\) (o espaço de entrada), como um conjunto \({\bf O}\) (o espaço de saída).
Mais tarde, vai ficar claro por que adicionamos um parâmetro de aleatoriedade específico.
Como exemplo, mostramos uma possibilidade de como esses conceitos podem ser mapeados para AES-GCM. Para cada tamanho de chave \(s_k\), tamanho de valor de uso único \(s_n\)e tamanho de tag válidos \(s_t\), o AES-GCM consiste em duas funções criptográficas, uma para criptografia e outra para descriptografia. Ambas vão ter o mesmo espaço de chave \({\bf K} = \{0,1\}^{s_k}\).
Para a função de criptografia \(\mathrm{Enc}\), os primeiros \(s_n\) bits de a aleatoriedade será utilizada para selecionar o valor de uso único.
Deixe \({\bf B} = \{0,1\}^8\) denotar um byte. O espaço de entrada da função de criptografia são os pares \({\bf I} = {\bf B}^{*} \times {\bf B}^{*}\) de pares de strings de bytes de tamanho arbitrário. O primeiro elemento do par é a mensagem, e o segundo é os dados associados. O padrão AES-GCM tem um limite superior no comprimento de nas entradas, mas preferimos permitir comprimentos arbitrários e, em vez disso, adicionar uma símbolo de erro \(\bot\) ao espaço de saída. O espaço de saída então se torna \({\bf O} = {\bf B}^* \cup \{\bot\}\), em que definimos arbitrariamente o resultado do cálculos bem-sucedidos como a concatenação \((\mathrm{IV} \| \mathrm{ciphertext} \| \mathrm{tag})\) conforme fornecida no padrão, e a saída \(\bot\), caso alguma entrada seja muito longa. Portanto, para uma chave fixa, a função de criptografia se torna do tipo \(\mathrm{Enc}_k : {\bf R} \times {\bf B}^* \times {\bf B}^* \rightarrow {\bf B}^* \cup \{\bot\}\).
Para a função de descriptografia \(\mathrm{Dec}\) o espaço da chave é o mesmo. A o espaço de entrada coincidentemente é o mesmo: \({\bf I} ={\bf B}^* \times {\bf B}^*\), mas agora o primeiro elemento será a saída da função de criptografia, enquanto o segundo ainda são os dados associados.
O espaço de saída também é o mesmo \({\bf O} = {\bf B}^* \cup \{\bot\}\) (de novo, coincidência). A interpretação é um pouco diferente, já que \(\bot\) geralmente indica um erro de autenticação (embora também seja o saída caso alguma entrada seja muito longa).
Enfatizamos que a formalização acima não é a única opção para formalizar o padrão. Por exemplo, é possível considerar o valor de uso único uma parte da entrada, em vez de lê-lo a partir da aleatoriedade, o que resulta em um primitivo muito diferente. Como alternativa, é possível definir a saída como um triplo contendo o valor de uso único, o texto criptografado e a tag (em vez da concatenação). Ou alguém poderia restringir o espaço da chave (um pouco arbitrariamente) para \({\bf K} = \{0,1\}^{128} \cup \{0,1\}^{256}\):
- Algoritmo criptográfico:
Um algoritmo criptográfico (simétrico) é uma tupla
\[(f_1, ... f_k)\]
de funções criptográficas, em que todas têm o mesmo espaço de chave. A O tipo do algoritmo criptográfico é a tupla \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\).
Por exemplo, para cada triplo \((s_k, s_n, s_t)\) válido de chave, valor de uso único e tag tamanho, o AES-GCM\({}_{s_k, s_n, s_t}\) é um algoritmo criptográfico com a duas funções \(\mathrm{Enc}\) e \(\mathrm{Dec}\) descritas acima.
Primitivos e interfaces
A seguir, definimos um primitivo criptográfico.
- Primário
- Um primitivo é um conjunto de algoritmos criptográficos, em que todos os algoritmos têm o mesmo tipo \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\)e os espaços de chave dos algoritmos são disjuntos em pares.
Por exemplo, considere o primitivo \(\mathrm{AEAD}\) na Tink. Ele tem várias algoritmos, entre eles estão AES-GCM para tamanhos de chave de 128 e 256 bits, com valor de uso único com 96 bits, AES-EAX com alguns tamanhos de chave e XChaCha20Poly1305. Eles têm espaços de chave separados, mas todos fornecem as mesmas funções criptográficas \(\mathrm{Enc}\) e \(\mathrm{Dec}\). Não vemos nenhum propósito recolher diferentes tamanhos-chave do AES-GCM nesta discussão formal, mas do curso poderia fazer isso).
Como definir primitivos
A forma mais comum de pensar nos primitivos é definir, antes de tudo, as propriedades as funções criptográficas e, depois, considerar o primitivo como esses algoritmos.
Por exemplo, para a AEAD, diríamos que \(\mathrm{Dec}_k(\mathrm{Enc}_k(m, a), a) = m\) é "sempre" satisfeito (exceto se o texto simples \(m\) for muito de comprimento). Além disso, temos propriedades de segurança. por exemplo, para uma chave aleatória, a criptografia é semi-ativamente segura.
Então, o primitivo da AEAD é simplesmente o conjunto de todos os algoritmos criptográficos que satisfazem essas propriedades. Em outras palavras, na prática, quando definimos primitivo, nós o definimos com base em propriedades. Não fornecemos uma lista de algoritmos, como a definição sugere.
Interfaces
Uma interface no Tink dá acesso a um primitivo, no sentido de que permite para calcular um elemento do espaço de saída a partir do espaço de entrada. Por exemplo: Considere a interface AEAD em Java:
public interface Aead {
byte[] encrypt(byte[] plaintext, byte[] associated_data) throws GeneralSecurityException;
byte[] decrypt(byte[] ciphertext, byte[] associated_data) throws GeneralSecurityException;
}
Observe que não damos acesso à aleatoriedade. Em vez disso, permitimos que o usuário fornecem elementos do espaço de entrada. Não permitir o acesso à aleatoriedade é de de propósito.1
O Tink às vezes oferece várias interfaces para um único primitivo.
Isso pode ser muito útil, já que os requisitos às vezes diferem. Ainda assim, fazer isso
tem um preço: em geral, quanto mais interfaces uma pessoa oferece, menor
é a interoperabilidade. Por exemplo, imagine
que alguém escreve uma biblioteca baseada no Tink que exige que o usuário transmita uma
Objeto Aead
(para criptografar algo internamente). Se o Tink oferecer muitos
interfaces diferentes para o \(\mathrm{AEAD}\) primitivo, é provável que
que o usuário
não tem uma instância pronta que funcione para a chave escolhida pelo usuário e a
ao mesmo tempo. Portanto, adicionar mais interfaces é uma troca.
-
As criptografias AEAD têm a propriedade de serem seguras contra ataques de texto criptografado escolhidos, o que é garantido apenas se não houver a reutilização do valor de uso único. A interface do Aead no Tink foi projetada de maneira que impede a reutilização do valor de uso único: o usuário não pode fornecer um valor de uso único como entrada para criptografia. um novo valor de uso único é gerado aleatoriamente para cada operação de criptografia. ↩