В криптографии есть основной принцип, согласно которому выходные данные могут быть детерминированными или недетерминированными. С детерминированным выводом мы всегда получаем один и тот же результат для одних и тех же входных данных. Например, мы можем создать шифр из открытого текста, и выходные данные всегда будут одним и тем же зашифрованным текстом. С недетерминированным, мы не можем предсказать, что будет использоваться в качестве выходных данных, поскольку в процессе была использована некоторая форма рандомизации. В случае с подписями, ECDSA является недетерминированным, а EdDSA — детерминированным.
Когда дело доходит до симметричных ключевых методов, детерминированная природа может быть полезна при поиске данных, но с точки зрения безопасности она будет слабее, поскольку злоумышленник может сопоставить входные данные с выходными. В этом случае мы объединим детерминированную природу с Authenticated Encryption with Associated Data (Deterministic AEAD) и будем использовать режим AES-SIV.
Мы выдаем слишком много наших секретов и часто не можем защитить один из наших самых важных секретов … наши ключи шифрования. Чтобы преодолеть это, мы можем использовать метод, называемый key wrapping, который защищает ключ. Это особенно важно, когда мы передаем ключ по ненадежным каналам или храним его в местах без строгого контроля доступа. Для key wrapping Rogaway et al предложили метод SIV (Synthetic Initialisation Vector) [here], который аутентифицирует и шифрует, а также аутентифицирует любые дополнительные данные, связанные с ключом:
В настоящее время этот метод стандартизирован в RFC 5297 [here]. С помощью улучшенных методов шифрования мы можем как аутентифицировать шифр, так и доказать его целостность. Это известно как Authenticated Encryption with Associated Data (AEAD). Для этого мы предоставляем дополнительные данные для аутентификации процесса шифрования и можем определить, где был изменен зашифрованный текст, чтобы его нельзя было расшифровать. В большинстве обычных методов AEAD мы создаем значение nonce и добавляем дополнительные данные (AD), которые аутентифицируются, но не шифруются. С подходом без nonce мы можем использовать метод key wrapping, который часто использовался для защиты ключей шифрования. Дополнительные данные могут включать [here]:
addresses, ports, sequence numbers, protocol version numbers, and other fields that indicate how the plaintext or ciphertext should be handled, forwarded, or processed
Таким образом, мы можем привязать сетевые пакеты к зашифрованным данным и обеспечить целостность, чтобы злоумышленник не мог копировать и вставлять действительный зашифрованный текст из других передач. Например, если мы привяжемся к номеру последовательности пакета и порту, аутентификация не удастся для другого номера последовательности или другого порта.
В следующем коде мы будем использовать дополнительную информацию «101112131415161718191a1b1c1d1e1f2021222324252627», но на практике она может быть любой длины. В SIV у нас также может быть несколько источников этих дополнительных данных (известных как вектор строк). Эта информация может быть распределена по нескольким источникам, и злоумышленнику, вероятно, будет трудно ее захватить и воспроизвести. Это одна из особенностей SIV, которая позволяет использовать несколько строк для аутентификации, а не объединять их в одну строку для получения дополнительной информации:
byte[] plaintext = pl.getBytes();
String additional="101112131415161718191a1b1c1d1e1f2021222324252627";
byte[] ciphertext = daead.encryptDeterministically(plaintext, Hex.decode(additional));
Традиционные методы AEAD могут быть слабыми с точки зрения повторного использования nonce, а также неправильного использования nonce. Во многих системах мы используем недетерминированный метод генерации nonce, поэтому нет возможности узнать, не использовались ли ранее сгенерированные значения. Другая слабость заключается в том, что nonce можно «воспроизвести», откатив виртуальную машину и обнаружив использованное значение nonce. Таким образом, большинство методов AEAD безопасны с уникальным nonce, но нет никакой гарантии безопасности, если nonce не является уникальным. Поскольку значение nonce ограничено по размеру, всегда будет шанс повторно использовать его с тем же ключом, и, следовательно, нам, возможно, придется регулярно менять наши ключи. SIV обеспечивает большую защиту от повторного использования/неправильного использования nonce, и злоумышленник может только определить, что данное значение открытого текста и данный набор связанных данных были защищены с использованием определенного ключа и значения nonce.
В следующем коде мы создаем ключ SIV, а затем создаем значения зашифрованного текста, которые должны быть одинаковыми [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)
}
Пример запуска показывает, что у нас один и тот же шифр, когда мы шифруем детерминированно [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