Потоковая передача AES-GCM-HKDF AEAD

В этом документе формально определяется математическая функция, представленная ключами потоковой передачи AES-GCM-HKDF, закодированная в прототипном формате как type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey .

Это шифрование во многом основано на HRRV15 1 . Для анализа безопасности мы обращаемся к HS20 2 .

Ключ и параметры

Ключи описываются следующими частями (все размеры в этом документе указаны в байтах):

  • \(\mathrm{KeyValue}\), байтовая строка.
  • \(\mathrm{CiphertextSegmentSize} \in \{1, 2, \ldots, 2^{31}-1\}\).
  • \(\mathrm{DerivedKeySize} \in \{16, 32\}\).
  • \(\mathrm{HkdfHashType} \in \{\mathrm{SHA1}, \mathrm{SHA256}, \mathrm{SHA512}\}\).

Действительные ключи дополнительно удовлетворяют следующим свойствам:

  • \(\mathrm{len}(\mathrm{KeyValue}) \geq \mathrm{DerivedKeySize}\).
  • \(\mathrm{CiphertextSegmentSize} > \mathrm{DerivedKeySize} + 24\) (Это равно \(\mathrm{len}(\mathrm{Header}) + 16\) как объяснено позже).

Ключи, которые не удовлетворяют ни одному из этих свойств, отклоняются Tink либо при анализе ключа, либо при создании соответствующего примитива.

Функция шифрования

Чтобы зашифровать сообщение \(\mathrm{Msg}\) со связанными данными\(\mathrm{AssociatedData}\)мы создаем заголовок, разбиваем сообщение на сегменты, шифруем каждый сегмент и объединяем зашифрованные сегменты.

Создайте заголовок

Мы выбираем однородную случайную строку \(\mathrm{Salt}\) длины\(\mathrm{DerivedKeySize}\) и однородная случайная строка \(\mathrm{NoncePrefix}\)длиной 7.

Затем мы устанавливаем \(\mathrm{Header} := \mathrm{len}(\mathrm{Header}) \| \mathrm{Salt} \| \mathrm{NoncePrefix}\), где длина заголовка кодируется как один байт. Обратите внимание, что \(\mathrm{len}(\mathrm{Header}) \in \{24, 40\}\).

Далее мы используем HKDF 3 с хеш-функцией, заданной выражением \(\mathrm{HkdfHashType}\)и входы \(\mathrm{ikm} := \mathrm{KeyValue}\), \(\mathrm{salt} := \mathrm{Salt}\), и \(\mathrm{info} := \mathrm{AssociatedData}\), с выходной длиной \(\mathrm{DerivedKeySize}\). Мы называем результат \(\mathrm{DerivedKey}\).

Разделить сообщение

Сообщение \(\mathrm{Msg}\) далее разбивается на части: \(\mathrm{Msg} = M_0 \| M_1 \| \cdots \| M_{n-1}\).

Их длина выбрана таким образом, чтобы удовлетворить:

  • \(\mathrm{len}(M_0) \in \{0,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{len}(\mathrm{Header}) - \mathrm{16}\}\).
  • Если \(n>1\), затем \(\mathrm{len}(M_1), \ldots, \mathrm{len}(M_{n-1}) \in \{1,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{16}\}\).
  • Если \(n>1\), затем \(\mathrm{len}(M_{0}), \ldots, \mathrm{len}(M_{n-2})\) должен иметь максимальную длину в соответствии с указанными выше ограничениями.

\(n\) может быть максимум \(2^{32}\). В противном случае шифрование не удастся.

Зашифруйте блоки

Чтобы зашифровать сегмент \(M_i\), мы вычисляем \(\mathrm{IV}_i := \mathrm{NoncePrefix} \| \mathrm{i} \| b\), где \(\mathrm{i}\) составляет 4 байта в кодировке с прямым порядком байтов, а байт $b$ равен 0x00 если $i < n-1$, и 0x01 в противном случае.

Затем мы шифруем \(M_i\) используя AES-GCM 4 , где ключ\(\mathrm{DerivedKey}\), вектор инициализации равен \(\mathrm{IV}_i\), а связанные данные — пустая строка. \(C_i\) является результатом этого шифрования (т.е. конкатенации \(C\) и \(T\) в разделе 5.2.1.2 связанного справочника по AES-GCM).

Объедините зашифрованные сегменты

Наконец, все сегменты объединяются как \(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\), который является окончательным зашифрованным текстом.

Расшифровка

Расшифровка инвертирует шифрование. Мы используем заголовок для получения\(\mathrm{NoncePrefix}\)и расшифровать каждый сегмент зашифрованного текста индивидуально.

API-интерфейсы могут (и обычно так и делают) разрешать произвольный доступ или доступ к началу файла без проверки конца файла. Это сделано намеренно, так как можно расшифровать \(M_i\) от \(C_i\), без расшифровки всех предыдущих и оставшихся блоков зашифрованного текста.

Однако API должны быть осторожны, чтобы не позволить пользователям путать ошибки конца файла и ошибки расшифровки: в обоих случаях API, вероятно, должен вернуть ошибку, и игнорирование разницы может привести к тому, что злоумышленник сможет эффективно обрезать файлы.

Сериализация и парсинг ключей

Чтобы сериализовать ключ в формате «Tink Proto», мы сначала сопоставляем параметры очевидным образом с прототипом, указанным в aes_gcm_hkdf_streaming.proto . version поля должна быть установлена ​​на 0. Затем мы сериализуем ее, используя обычную сериализацию прототипа, и встраиваем полученную строку в значение поля прототипа KeyData . Мы устанавливаем для поля type_url type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey . Затем мы устанавливаем key_material_type в SYMMETRIC и встраиваем его в набор ключей. Обычно мы устанавливаем для output_prefix_type значение RAW . Исключением является то, что если ключ был проанализирован с другим значением, установленным для output_prefix_type , Tink может записать либо RAW , либо предыдущее значение.

Чтобы разобрать ключ, мы обращаем описанный выше процесс (обычным способом при разборе прото). Поле key_material_type игнорируется. Значение output_prefix_type можно либо игнорировать, либо ключи, у которых output_prefix_type отличается от RAW могут быть отклонены. Ключи, version которых отличается от 0, должны быть отклонены.

Известные проблемы

Ожидается, что реализации вышеуказанной функции шифрования не будут безопасными для вилки. См. Безопасность вилки .

Ссылки


  1. Хоанг, Рейханитабар, Рогавей, Визар, 2015. Онлайн-шифрование с аутентификацией и его устойчивость к неправильному использованию при повторном использовании. КРИПТО 2015. https://eprint.iacr.org/2015/189

  2. Хоанг, Шен, 2020. Безопасность потокового шифрования в библиотеке Google Tink. https://eprint.iacr.org/2020/1019

  3. RFC 5869. Функция извлечения и расширения ключа на основе HMAC (HKDF). https://www.rfc-editor.org/rfc/rfc5869

  4. НИСТ СП 800-38D. Рекомендации по режимам работы блочного шифрования: режим Галуа/счетчика (GCM) и GMAC. https://csrc.nist.gov/pubs/sp/800/38/d/final