Existe um princípio fundamental na criptografia, onde uma saída pode ser determinística ou não determinística. Com uma saída determinística, sempre obtemos a mesma saída para as mesmas entradas. Por exemplo, podemos criar uma cifra a partir de texto simples, onde a saída será sempre o mesmo texto cifrado. Com não determinística, não podemos prever qual será a saída usada, pois alguma forma de randomização foi usada no processo. Com assinaturas, ECDSA é não determinística e EdDSA é determinística.
Quando se trata de métodos de chave simétrica, a natureza determinística pode ser útil na busca de dados, mas será mais fraca do ponto de vista da segurança, pois um adversário pode mapear as entradas para a saída. Neste caso, combinaremos a natureza determinística com a Criptografia Autenticada com Dados Associados (Deterministic AEAD) e usaremos o modo AES-SIV.
Revelamos um pouco demais de nossos segredos e, muitas vezes, não protegemos um de nossos segredos mais importantes… nossas chaves de criptografia. Para superar isso, podemos usar um método chamado key wrapping, que protege a chave. Isso é especialmente importante quando transmitimos a chave por canais não confiáveis ou a armazenamos em locais sem forte controle de acesso. Para um key wrapping, Rogaway et al propuseram o método SIV (Synthetic Initialisation Vector) [here] que autentica e criptografa, juntamente com a autenticação de quaisquer dados adicionais relacionados à chave:
O método agora é padronizado com RFC 5297 [here]. Com métodos de criptografia aprimorados, podemos autenticar a cifra e provar sua integridade. Isso é conhecido como Criptografia Autenticada com Dados Associados (AEAD). Para isso, fornecemos dados adicionais para autenticar o processo de criptografia e podemos identificar onde o texto cifrado foi modificado, para que não possa ser descriptografado. Com a maioria dos métodos AEAD convencionais, criamos um valor nonce e adicionamos dados adicionais (AD) que são autenticados, mas não criptografados. Com uma abordagem sem nonce, podemos usar um método key wrapping, que tem sido frequentemente usado para proteger chaves de criptografia. Os dados adicionais podem incluir [here]:
addresses, ports, sequence numbers, protocol version numbers, and other fields that indicate how the plaintext or ciphertext should be handled, forwarded, or processed
Desta forma, podemos vincular pacotes de rede aos dados criptografados e fornecer integridade para que um intruso não possa copiar e colar texto cifrado válido de outras transmissões. Por exemplo, se nos vincularmos a um número de sequência de pacotes e à porta, a autenticação falhará para outro número de sequência ou outra porta.
No código a seguir, usaremos as informações adicionais de "101112131415161718191a1b1c1d1e1f2021222324252627", mas, na prática, isso pode ter qualquer comprimento. Dentro do SIV, também, podemos ter múltiplas fontes desses dados adicionais (conhecido como um vetor de strings). Essas informações podem ser espalhadas por múltiplas fontes e provavelmente serão difíceis para um intruso capturar e replicar. Esta é uma das características especiais do SIV e permite que múltiplas strings sejam usadas para a autenticação, em vez de ter que mesclá-las em uma única string para informações adicionais:
byte[] plaintext = pl.getBytes();
String additional="101112131415161718191a1b1c1d1e1f2021222324252627";
byte[] ciphertext = daead.encryptDeterministically(plaintext, Hex.decode(additional));
Os métodos AEAD tradicionais podem ser fracos em termos de reutilização de nonce e também de uso indevido de nonce. Em muitos sistemas, usamos um método não determinístico de gerar um nonce, então não há como saber se valores gerados anteriormente não foram usados antes. Outra fraqueza é onde um nonce pode ser "reproduzido" revertendo uma máquina virtual e descobrindo o valor nonce usado. Assim, a maioria dos métodos AEAD são seguros com um nonce exclusivo, mas onde não há garantia de segurança se o nonce não for exclusivo. Como o valor nonce é limitado em tamanho, sempre haverá uma chance de reutilizá-lo com a mesma chave e, portanto, podemos ter que alterar nossas chaves regularmente. O SIV fornece mais proteção para reutilização/uso indevido de nonce, e onde um invasor só pode determinar que um determinado valor de texto simples e um determinado conjunto de dados associados foram protegidos usando a chave e o valor nonce definidos.
No código a seguir, criamos uma chave SIV e, em seguida, criamos valores de texto cifrado, que devem ser os mesmos [here]:
package main
import (
"fmt"
"os"
"github.com/tink-crypto/tink-go/v2/daead"
"github.com/tink-crypto/tink-go/v2/keyset"
)
func main() {
msg:="Hello"
associated:="Hello"
argCount := len(os.Args[1:])
if (argCount>0) {msg = os.Args[1]}
if (argCount>1) {associated =os.Args[2]}
handle, _ := keyset.NewHandle(daead.AESSIVKeyTemplate())
primitive, _ := daead.New(handle)
plaintext := []byte(msg)
associatedData := []byte(associated)
ciphertext1, _:= primitive.EncryptDeterministically(plaintext, associatedData)
ciphertext2, _:= primitive.EncryptDeterministically(plaintext, associatedData)
res, _ := primitive.DecryptDeterministically(ciphertext1, associatedData)
fmt.Printf("Plaintext is %s\n",msg)
fmt.Printf("Associated data is %s\n\n",associated)
fmt.Printf("Key is %s\n\n",handle)
fmt.Printf("Cipher1 is %x\n\n",ciphertext1)
fmt.Printf("Cipher2 is %x\n\n",ciphertext2)
fmt.Printf("Decrypted is %s",res)
}
Uma execução de amostra mostra que temos a mesma cifra quando criptografamos deterministicamente [here]:
Plaintext is Testing 123
Associated data is qwerty123
Key is primary_key_id:1537855825 key_info:{type_url:"type.googleapis.com/google.crypto.tink.AesSivKey" status:ENABLED key_id:1537855825 output_prefix_type:TINK}
Cipher1 is 015ba9d1518f3a5d213ce9630294f87af76ce86011c8631842aa96571d0dee03
Cipher2 is 015ba9d1518f3a5d213ce9630294f87af76ce86011c8631842aa96571d0dee03
Decrypted is Testing 123