По устаревшим причинам одна из моих систем не имеет возможности использовать режим AEAD, мы ограничены AES в обычном режиме CBC или CTR плюс MAC.
Типичная задача — передать данные с одного узла на другой, гарантируя целостность и конфиденциальность. Я ловлю себя на том, что постоянно указываю следующую композицию:
- CSPRNG для создания секрета начальной загрузки
- KDF для получения ключей для шифрования и MAC — я использую HKDF
- CSPRNG снова получить IV
- Режим CTR для шифрования данных
- MAC по секрету начальной загрузки, iv, cipherspec и шифротексту — я использую HMAC
Итак, я делаю «Зашифровать-затем-MAC» и аутентифицирую все входные данные для вычисления зашифрованного текста. Но я как бы беспечно предположил, что эта композиция безопасна.
На самом деле я никогда не видел эту полную композицию, включая KDF, описанную как многоразовый примитив.
TLS делает что-то очень похожее, но это не совсем то же самое (например, он по-другому использует HKDF).
Схемы IES, такие как ECIES и DLIES, выглядят концептуально похожими, но различаются в деталях, особенно в способе получения входных данных для KDF.
Итак, мой вопрос: является ли эта проблема недостаточно общей, чтобы гарантировать решение поваренной книги? Или, может быть, что-то уже существует, что я упустил из виду? Иначе как я могу обрести уверенность в решении? (Когда дело доходит до криптографии, я всегда осторожен).
В случае, если детали полезны, поток:
Передающий узел выполняет следующие действия:
- Получите 256 секретных битов
семя
из CSPRNG
- Шифровать
семя
для другого узла, использующего его открытый ключ как зашифрованное_начальное число
- Расколоть
семя
в 128 бит соль
и 128 бит key_material
- Получите 384 секретных бита, вызвав
HKDF-HMAC-SHA-256(length=384b, ikm=key_material, salt=salt, info=<идентификатор исходного узла || идентификатор конечного узла>)
- Разделить на 128 бит
ключ_шифрования
, 256 бит HMAC_ключ
Для каждого отправляемого сообщения:
- Получите 128 бит из CSPRNG как
шифрование_iv
- Зашифруйте открытый текст, используя
AES-128-CTR(iv=шифрование_iv, ключ=шифрование_ключ)
- Вычислить тег как
HMAC-SHA-256 (ключ = ключ_HMAC, данные = зашифрованное_начальное число || шифрование_iv || шифроспецификация = AES-128-CTR || зашифрованный текст)
- Отправить на другой узел:
зашифрованное_начальное число || шифрование_iv || спецификация шифра || зашифрованный текст || тег
Принимающий узел выполняет следующие действия:
- Разобрать полученное сообщение на его компоненты
зашифрованное_начальное число
и т.д.
- расшифровать
зашифрованное_начальное число
используя закрытый ключ принимающего узла, получая семя
- Расколоть
семя
в 128 бит соль
и 128 бит key_material
- Получите 384 секретных бита, вызвав
HKDF-HMAC-SHA-256(length=384b, ikm=key_material, salt=salt, info=<идентификатор исходного узла || идентификатор конечного узла>)
- Разделить на 128 бит
ключ_шифрования
, 256 бит HMAC_ключ
- Вычислить тег как
HMAC-SHA-256 (ключ = HMAC_key, данные = <сообщение, полученное от отправляющего узла без тега>)
- Назначать
tag_valid := правда
если тег совпадает с тегом в полученном сообщении, ЛОЖЬ
в противном случае
- Назначать
k := ключ_шифрования
если tag_valid
, иначе присвоить k := <некоторая случайная константа>
- Расшифровать зашифрованный текст как
[спецификация шифра](iv=encryption_iv, ключ=k)
- Вывести кортеж
(tag_valid, открытый текст)
- звонящий должен проверить tag_valid
перед использованием открытого текста
Так что же может пойти не так? Ну, для одного семя
значение используется до проверки тега MAC. Я мог бы подписать его, используя закрытый ключ отправляющего узла, но на самом деле это не связывает семя
к тегу MAC. Кроме того, это начинает становиться грязным, отсюда и мое беспокойство.