Перейти к содержимому
ecdsa.com
← Все статьи

X25519MLKEM768: гибридный обмен ключами в TLS 1.3

Один key share несёт ключ ML-KEM-768 и ключ X25519, и оба секрета попадают в расписание ключей. Ниже — побайтовая структура, клиенты, которые уже его отправляют, способ проверить гибрид на собственном сервере и старое сетевое оборудование, которое с ним не справляется.

Коротко. X25519MLKEM768 — группа обмена ключами TLS 1.3 с кодовым значением 0x11EC (4588): она параллельно выполняет ML-KEM-768 и X25519 и передаёт оба общих секрета в расписание ключей. Группа стандартизирована в RFC 10024 (август 2026 года). Chrome, Firefox, сетевые API Apple в релизах версии 26, OpenSSL 3.5, Go 1.24+ и JDK 27 предлагают её по умолчанию. Запустите openssl s_client -connect example.com:443 -brief с OpenSSL 3.5 и найдите в выводе строку Negotiated TLS1.3 group: X25519MLKEM768. Группа защищает записанный трафик от будущего квантового компьютера. Для аутентификации она не делает ничего: аутентификация по-прежнему опирается на сертификаты RSA или ECDSA.

Что означает название

В TLS 1.3 обмен ключами согласуется через именованные группы (named groups). Клиент перечисляет поддерживаемые группы в расширении supported_groups и отправляет в key_share открытые ключи для одной или нескольких из них. Сервер выбирает одну группу и отвечает собственным key share. Многие годы реальный выбор сводился к X25519 и ECDH на P-256. Обе схемы взламываются алгоритмом Шора на достаточно большом квантовом компьютере, а трафик, записанный сегодня, можно будет расшифровать, как только такая машина появится. В этом и состоит проблема «собери сейчас, расшифруй потом» (harvest now, decrypt later).

Гибридная группа решает её, не делая ставку целиком на новую математику. X25519MLKEM768 выполняет два согласования ключей в рамках одного согласования группы:

  • ML-KEM-768 — механизм инкапсуляции ключа на основе модульных решёток из FIPS 203 (август 2024 года) с параметрами категории 3. Эта часть устойчива к квантовым атакам.
  • X25519 — функция Диффи — Хеллмана на эллиптической кривой из RFC 7748. У неё долгая история криптоанализа, и она подстраховывает более новый компонент.

Общая конструкция описана в RFC 9954 (Гибридный обмен ключами в TLS 1.3, июль 2026 года). Его заявленная цель — безопасность, которая сохраняется, «даже если будет найден способ взломать шифрование всех алгоритмов-компонентов, кроме одного». Конкретные группы определены в RFC 10024 (август 2026 года):

ГруппаКодовое значениеРекомендованаКомпоненты
X25519MLKEM7680x11EC (4588)ДаML-KEM-768 + X25519
SecP256r1MLKEM7680x11EB (4587)НетP-256 ECDH + ML-KEM-768
SecP384r1MLKEM10240x11ED (4589)НетP-384 ECDH + ML-KEM-1024

В старых дампах трафика может встретиться X25519Kyber768Draft00 с кодовым значением 0x6399 (25497). Это достандартный гибрид с Kyber, который Chrome внедрил первым. В FIPS 203 алгоритм изменили в мелочах, но несовместимым образом, поэтому в Chrome 131 его заменили на 0x11EC, а группу из черновика убрали. В объявлении Google объясняется, почему обе группы не поддерживались одновременно: «Постквантовая криптография слишком громоздка, чтобы можно было одновременно отправлять два постквантовых key share на основе прогноза». RFC 10024 помечает кодовые значения из черновиков как устаревшие.

Что внутри key share

Гибридный key share — простая конкатенация двух значений-компонентов без префиксов длины внутри. Размеры зафиксированы в FIPS 203 и RFC 7748, поэтому каждое поле можно проверить по одной только длине:

ПолеСтруктураРазмер, байт
key_exchange клиентаКлюч инкапсуляции ML-KEM-768 (1184) ‖ открытый ключ X25519 (32)1216
key_exchange сервераШифртекст ML-KEM-768 (1088) ‖ открытый ключ X25519 (32)1120
Общий секретОбщий секрет ML-KEM (32) ‖ общий секрет X25519 (32)64

Обратите внимание на порядок. Название начинается с X25519, а байты — с ML-KEM. В RFC 10024 сказано, что порядок «изменён на обратный» по сравнению с соглашением об именовании «по историческим причинам». В разделе 5 приводится и довод, связанный с соответствием требованиям: NIST SP 800-56C разрешает использовать объединённый секрет в одобренной FIPS процедуре формирования ключа, только если первая часть входных данных получена по одобренной схеме, а в этой паре такая схема — ML-KEM. Два гибрида на кривых NIST по той же причине ставят первым key share ECDH. Парсер, который делит поле на границе 32-го байта, а не 1184-го, будет ошибаться каждый раз.

Вот настоящий ClientHello, который OpenSSL 3.5.7 отправил на cloudflare.com; он записан с параметром -trace 20 октября 2026 года (байты ключей сокращены):

openssl s_client -trace, ClientHello (сокращено)
Sent TLS Record
  Content Type = Handshake (22)
  Length = 1551
    ClientHello, Length=1547
        extension_type=supported_groups(10), length=18
          X25519MLKEM768 (4588)
          ecdh_x25519 (29)
          secp256r1 (P-256) (23)
          ecdh_x448 (30)
          secp384r1 (P-384) (24)
          secp521r1 (P-521) (25)
          ffdhe2048 (256)
          ffdhe3072 (257)
        extension_type=key_share(51), length=1258
            NamedGroup: X25519MLKEM768 (4588)
            key_exchange:  (len=1216): A4696B297C695DEA602FB55157BC359F...
            NamedGroup: ecdh_x25519 (29)
            key_exchange:  (len=32): 919D957E3D7E402ECF27C3DD0B38DDA0...

Клиент отправляет два key share: гибридный и обычный X25519. Так OpenSSL 3.5 ведёт себя по умолчанию. Go 1.27.3 отправляет ту же пару, и Chromium 156 тоже: он добавляет элемент GREASE, и его ClientHello приходит в записи размером 1704 байта. JEP 527 задаёт ту же пару для JDK 27. Обычный key share обходится в 32 байта и позволяет серверу, который не знает 0x11EC, завершить рукопожатие за один круг обмена сообщениями (round trip), не запрашивая другой key share. Размер расширения key_share сходится точно: 2 байта на длину списка, затем 2 + 2 + 1216 на гибридный элемент и 2 + 2 + 32 на X25519, всего 1258. Сервер остановился на гибриде:

openssl s_client -trace, ServerHello (сокращено)
    ServerHello, Length=1206
        extension_type=key_share(51), length=1124
            NamedGroup: X25519MLKEM768 (4588)
            key_exchange:  (len=1120): C01BC77BB1F9985B0DE82CDD1D0DD14B...

Для сравнения: тот же клиент с -groups X25519 отправляет ClientHello размером 313 байт. Гибрид добавляет примерно 1,2 КБ в каждом направлении, и именно этот размер вызывает большинство проблем совместимости (см. ниже).

От двух секретов к одному расписанию ключей

Каждая сторона выполняет две независимые операции:

  1. Клиент генерирует пару ключей ML-KEM-768 и пару ключей X25519 и отправляет оба открытых ключа.
  2. Сервер выполняет инкапсуляцию с ключом ML-KEM и получает 32-байтный секрет и 1088-байтный шифртекст. Кроме того, он выполняет X25519 со своим эфемерным ключом, после чего отправляет шифртекст и свой открытый ключ X25519.
  3. Клиент выполняет декапсуляцию шифртекста, получая тот же секрет ML-KEM, и завершает X25519 на своей стороне.
  4. Обе стороны объединяют результаты в 64 байта и используют это значение там, где в TLS 1.3 обычно стоит секрет (EC)DHE: как входные данные для HKDF-Extract, который выводит секрет рукопожатия (RFC 9846 § 7.1 — редакция TLS 1.3 от июля 2026 года, которая заменяет RFC 8446).

Всё дальнейшее — обычный TLS 1.3. Наборы шифров, сертификат, CertificateVerify и ключи трафика не меняются. RFC 10024 добавляет шаги проверки, которые реализации обязаны выполнять. Они проверяют ключ инкапсуляции так, как требует FIPS 203, отклоняют шифртекст неправильной длины и прерывают работу, если результат X25519 состоит из одних нулей. Любая такая ошибка завершает рукопожатие оповещением illegal_parameter или internal_error.

Обоснование безопасности короткое. Атакующему, который записал рукопожатие, нужно восстановить оба секрета, чтобы вычислить секрет рукопожатия. Взлома X25519 будущим квантовым компьютером недостаточно, пока держится ML-KEM. Неожиданного криптоаналитического прорыва против молодой схемы на основе решёток недостаточно, пока держится X25519. Если хотите увидеть инкапсуляцию и декапсуляцию на настоящих ключах, их можно выполнить прямо в браузере в лаборатории ML-KEM.

Кто включает гибрид по умолчанию

По состоянию на октябрь 2026 года все основные клиенты предлагают X25519MLKEM768 по умолчанию:

ПОПо умолчанию сИсточник
Chrome (настольная версия)Chrome 131, вместо группы на основе черновика KyberGoogle Security Blog
FirefoxПоддерживается с Firefox 132 (TLS) и Firefox 135 (HTTP/3). Параметр security.tls.enable_kyber по умолчанию равен trueПримечания к выпуску
Apple: URLSession и фреймворк NetworkРелизы версии 26: iOS, iPadOS, macOS Tahoe, visionOS (по руководству по подготовке сети), а также tvOS и watchOS (по руководству по безопасности)Поддержка Apple, Безопасность платформ
OpenSSL3.5.0: гибридные группы в списке по умолчанию, key share по умолчанию — X25519MLKEM768 + X25519NEWS.md
Go crypto/tlsGo 1.24, если Config.CurvePreferences равно nil. В Go 1.26 добавлены два гибрида на кривых NISTGo 1.24, Go 1.26
Java (JSSE)JDK 27, JEP 527: X25519MLKEM768 стоит первой в списке по умолчаниюJEP 527

Всё, что собрано с OpenSSL 3.5, тоже получает это значение по умолчанию, если конфигурация не переопределяет список групп. Это относится к пакетам Debian 13 и к Node.js 24, который поставляется с собственной копией OpenSSL 3.5. В Debian 13 модуль ssl из Python завершил рукопожатие с openssl s_server -groups X25519MLKEM768, который не принимает ничего, кроме этой группы, а curl и nginx согласовали гибрид без дополнительной настройки. На странице Apple о безопасности TLS отмечен один пробел: «TLS-клиенты, использующие API SecureTransport, не могут работать с TLS 1.3», поэтому приложения, которые всё ещё используют этот API, гибридного обмена ключами не получают.

На стороне серверов значительная доля HTTPS-трафика уже терминируется на стеках, которые выбирают гибрид, когда клиент его предлагает. 20 октября 2026 года при проверке с OpenSSL 3.5.7 группа X25519MLKEM768 была согласована с cloudflare.com, google.com, www.amazon.com, www.microsoft.com, www.apple.com и example.com, а обычный X25519 — с github.com и letsencrypt.org.

Как проверить сервер, клиент и браузер

Из командной строки. В выводе OpenSSL 3.5 с -brief группа названа прямо:

openssl s_client -brief (OpenSSL 3.5.7, 20 октября 2026 года)
$ openssl s_client -connect cloudflare.com:443 -servername cloudflare.com -brief </dev/null
Connecting to 104.16.132.229
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Peer certificate: CN=cloudflare.com
Hash used: SHA256
Signature type: ecdsa_secp256r1_sha256
Verification: OK
Negotiated TLS1.3 group: X25519MLKEM768
DONE

При соединении с сервером, который выбирает классическую группу, в выводе вместо этого будет строка Peer Temp Key: X25519, 253 bits. Чтобы проверить, умеет ли сервер вообще работать с гибридом, не предлагайте ничего другого. Сервер, который поддерживает только классические группы, тогда прервёт рукопожатие:

Только гибрид, сервер без его поддержки (сокращено)
$ openssl s_client -groups X25519MLKEM768 -connect github.com:443 -servername github.com -brief </dev/null
...:error:0A000410:SSL routines:ssl3_read_bytes:ssl/tls alert handshake failure:...:SSL alert number 40

Используйте такую проверку только для тестирования. Настоящий клиент, который предлагает только гибрид, жертвует совместимостью без всякой выгоды. curl, собранный с OpenSSL 3.5, выводит группу в строке о соединении в подробном режиме:

curl -v (curl 8.14.1, OpenSSL 3.5.7)
$ curl -sv -o /dev/null https://www.google.com/ 2>&1 | grep 'SSL connection'
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519MLKEM768 / id-ecPublicKey

Из кода. Go 1.25 и новее отдают согласованную группу как ConnectionState().CurveID, а Node.js 24 сообщает её через getEphemeralKeyInfo():

Go: вывести согласованную группу
conn, err := tls.Dial("tcp", "cloudflare.com:443", &tls.Config{ServerName: "cloudflare.com"})
if err != nil {
	log.Fatal(err)
}
fmt.Println(conn.ConnectionState().CurveID) // X25519MLKEM768 (Go 1.27.3)
Node.js 24: вывести согласованную группу
import tls from "node:tls";
const socket = tls.connect({ host: "cloudflare.com", port: 443, servername: "cloudflare.com" }, () => {
  console.log(socket.getEphemeralKeyInfo()); // { type: 'TLSGroup', name: 'X25519MLKEM768', size: undefined } (Node 24.22.0)
  socket.end();
});

В браузере. Chrome показывает группу в DevTools, в подробностях о соединении на панели безопасности. То же значение доступно программно как securityDetails.keyExchangeGroup в событии Network.responseReceived протокола DevTools. 20 октября 2026 года Chromium 156 в режиме headless показал X25519MLKEM768 для www.cloudflare.com и X25519 для github.com. Для macOS 26 Apple предлагает команду nscurl --tls-diagnostics https://your.host. Чтобы узнать, что отправляет ваш браузер, откройте проверку постквантового TLS в браузере.

Как включить гибрид на своём сервере

Если в основе стоит OpenSSL 3.5, значение по умолчанию уже правильное. Обычно его теряют из-за явного списка групп, скопированного из старого руководства по усилению защиты. В этом тесте nginx 1.30.5 в Debian 13 (OpenSSL 3.5.7) меняется только строка ssl_ecdh_curve, а проверка выполняется через openssl s_client -brief:

nginx 1.30.5: группа зависит от ssl_ecdh_curve (только вывод проверки, строки журнала nginx опущены)
--- no ssl_ecdh_curve (OpenSSL default list)
Protocol version: TLSv1.3
Negotiated TLS1.3 group: X25519MLKEM768
--- ssl_ecdh_curve X25519:prime256v1:secp384r1;
Protocol version: TLSv1.3
Peer Temp Key: X25519, 253 bits
--- ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1:secp384r1;
Protocol version: TLSv1.3
Negotiated TLS1.3 group: X25519MLKEM768

Значит, исправить можно одним из двух способов: удалить строку или поставить в ней X25519MLKEM768 первой. Старые выпуски OpenSSL не знают этого имени, поэтому сначала обновите TLS-библиотеку. nginx 1.24.0 на OpenSSL 1.1.1w отклоняет такую строку (здесь ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1;) с ошибкой nginx: [emerg] SSL_CTX_set1_curves_list("X25519MLKEM768:X25519:prime256v1") failed. В других серверах то же самое настраивается под другими именами: SSLOpenSSLConfCmd Groups в Apache httpd, curves в HAProxy, CurvePreferences в Go, jdk.tls.namedGroups в Java. Если нужна отправная точка для конкретного сервера, генератор конфигурации TLS создаёт конфигурации, в которых гибридные группы уже на месте.

Ещё о двух местах легко забыть:

  • Исходный сервер за CDN или балансировщиком нагрузки. Соединение браузера заканчивается на пограничном узле, а соединение от пограничного узла до вашего исходного сервера — отдельный TLS-сеанс со своим согласованием. Трафик на этом втором участке точно так же можно записать сейчас и расшифровать потом.
  • Внутренний TLS между сервисами. Сервисы на Go получают гибрид от версии инструментария Go, которым они собраны. Сервисы на Java получают его с JDK 27, где JEP 527 добавил гибридные группы. Там, где жёстко задан CurvePreferences, jdk.tls.namedGroups или другой явный список групп, гибрида не будет, пока кто-нибудь этот список не отредактирует.

Прогноз key share и HelloRetryRequest

Клиент TLS 1.3 должен угадать, какую группу выберет сервер, и заранее отправить key share для неё. Если клиент ошибся, сервер отвечает сообщением HelloRetryRequest с названием нужной ему группы, и клиент начинает заново. Это стоит целого круга обмена сообщениями. С постквантовыми key share угадать важнее, потому что отправлять key share размером 1216 байт «на всякий случай» не бесплатно. Повторный запрос можно увидеть, если велеть OpenSSL указать гибрид в списке, но отправить key share только для X25519:

openssl s_client -groups X25519:X25519MLKEM768 -trace (сокращено)
    ClientHello, Length=315
        extension_type=key_share(51), length=38
            NamedGroup: ecdh_x25519 (29)
    ServerHello, Length=84                      <- HelloRetryRequest
        extension_type=key_share(51), length=2
            NamedGroup: X25519MLKEM768 (4588)
    ClientHello, Length=1499
        extension_type=key_share(51), length=1222
            NamedGroup: X25519MLKEM768 (4588)
    ServerHello, Length=1206
Negotiated TLS1.3 group: X25519MLKEM768

Сервер отдал предпочтение гибриду, хотя клиент поставил X25519 первым в списке, поэтому и запросил повтор. В строках групп OpenSSL 3.5 префикс * отмечает группы, для которых key share отправляется сразу, в первом ClientHello. -groups '*X25519:*X25519MLKEM768' отправляет оба key share в ClientHello размером 1535 байт и обходится без повтора. Основные клиенты по умолчанию отправляют оба key share по той же причине. Черновик рабочей группы TLS draft-ietf-tls-key-share-prediction позволяет серверам публиковать предпочтительные группы в DNS-записях типа HTTPS, чтобы клиенты угадывали с первого раза. В октябре 2026 года он всё ещё оставался черновиком (Internet-Draft).

Что ломается: большие ClientHello и старые промежуточные устройства

Измеренный выше ClientHello заполняет TLS-запись полезной нагрузкой размером 1551 байт. Вместе с заголовками записи, TCP и IP он уже не помещается в один пакет на типичном пути с MTU 1500 байт. Спецификация всегда это допускала, но некоторые реализации рассчитывали, что первый read() из сокета вернёт весь ClientHello. Они ломаются, когда из-за гибрида ClientHello разбивается на два сегмента.

Проект tldr.fail отслеживает этот класс ошибок. Он описывает серверы, которые «не готовы к тому, что для чтения всего ClientHello read() придётся вызвать больше одного раза». В его списке есть прокси с инспекцией TLS, межсетевые экраны и балансировщики нагрузки нескольких производителей. Часть из них с тех пор исправили, у части статус помечен как неизвестный. Симптомы похожи на сетевые неполадки, а не на проблемы с криптографией: ERR_CONNECTION_RESET или ERR_SSL_PROTOCOL_ERROR в Chrome, тайм-ауты рукопожатия или сброс соединения сразу после ClientHello. Часто ошибка проявляется только у клиентов, которые отправляют гибрид. Apple предупреждает о том же сбое в примечаниях к релизам версии 26: устройства могут не подключаться к некоторым старым серверам, чей код TLS «не может прочитать большие сообщения ClientHello».

Если вы столкнулись с этой проблемой:

  1. Найдите устройство, которое сбрасывает соединение. Сравните openssl s_client -groups X25519 со списком групп по умолчанию на одном и том же сетевом пути. Если сбой происходит только с большим ClientHello, проблема в размере, а не в сертификате.
  2. Обновите или замените это устройство. Если отключить гибрид на клиентах, защита пропадёт для всех соединений, включая те, что работали.
  3. Используйте переключатели на стороне клиента только как временное решение. В Go есть GODEBUG=tlsmlkem=0, и в примечаниях к Go 1.24 он упоминается для случая «некорректно работающих TLS-серверов, которые неправильно обрабатывают большие записи». В Chrome была корпоративная политика PostQuantumKeyAgreementEnabled; Google описывал её как «временную меру», которая «будет удалена через некоторое время после версии Google Chrome 145». Прежде чем строить на ней планы, проверьте, учитывает ли её ещё ваша версия Chrome.

О прокси, которые терминируют TLS, нужно сказать ещё одно. Прокси, расшифровывающий трафик, проводит два рукопожатия. То, что ваш ноутбук согласовал с прокси, ничего не говорит о соединении прокси с внешним миром. Этот участок будет постквантовым, только если собственный TLS-стек прокси поддерживает гибрид и предпочитает его.

От чего гибрид не защищает

X25519MLKEM768 защищает конфиденциальность от противника, который записывает трафик сейчас и расшифровывает его потом. Аутентификацию в рукопожатии эта группа постквантовой не делает. Сервер по-прежнему подтверждает свою подлинность сертификатом RSA или ECDSA и подписью в CertificateVerify. Тот, кто сможет подделать эти подписи с помощью квантового компьютера, сможет выдать себя за сервер прямо во время соединения. Эта угроза появится только вместе с таким компьютером, поэтому обмен ключами перевели первым, а подписи — отдельная, более медленная миграция. Эта работа разобрана в других статьях серии: ключи и подписи ML-DSA — в статье ML-DSA для разработчиков, а общий план с инвентаризацией и сроками — в статье Постквантовая миграция в 2026 году.

Кроме того, гибрид ничего не даёт трафику, который идёт не по TLS 1.3. У соединений TLS 1.2, сеансов SSH и VPN-туннелей свои механизмы обмена ключами и свои пути перехода. Прежде чем считать соединения защищёнными, выясните, какие из них всё ещё согласуют TLS 1.2.

Источники

  • RFC 10024: «Гибридные механизмы согласования ключей для TLS 1.3, сочетающие постквантовые и традиционные алгоритмы (PQ/T)» (август 2026 года). Определения групп, порядок байтов, проверки, кодовые значения IANA.
  • RFC 9954: «Гибридный обмен ключами в TLS 1.3» (июль 2026 года).
  • FIPS 203: «Стандарт механизма инкапсуляции ключа на основе модульных решёток» (август 2024 года).
  • RFC 9846 § 7.1: расписание ключей TLS 1.3.
  • Google Security Blog, Новый путь для Kyber в вебе (сентябрь 2024 года).
  • Поддержка Apple, Подготовка сети к квантово-устойчивому шифрованию в TLS, и руководство «Безопасность платформ Apple».
  • NEWS.md OpenSSL 3.5, примечания к выпускам Go 1.24 и 1.26, JEP 527.
  • tldr.fail: ошибка с большим ClientHello и затронутые продукты.

Весь вывод команд на этой странице получен 20 октября 2026 года в контейнерах: OpenSSL 3.5.7, curl 8.14.1, Node.js 24.22.0 и Python 3 из Debian — в node:24.22.0-trixie, Go 1.27.3 — в golang:1.27.3-trixie, nginx 1.30.5 — в nginx:1.30.5, nginx 1.24.0 с OpenSSL 1.1.1w — в nginx:1.24.0, Chromium 156 — в mcr.microsoft.com/playwright:v1.64.1-noble.