Коротко. 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 года):
| Группа | Кодовое значение | Рекомендована | Компоненты |
|---|---|---|---|
| X25519MLKEM768 | 0x11EC (4588) | Да | ML-KEM-768 + X25519 |
| SecP256r1MLKEM768 | 0x11EB (4587) | Нет | P-256 ECDH + ML-KEM-768 |
| SecP384r1MLKEM1024 | 0x11ED (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 года (байты ключей сокращены):
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. Сервер остановился на гибриде:
ServerHello, Length=1206
extension_type=key_share(51), length=1124
NamedGroup: X25519MLKEM768 (4588)
key_exchange: (len=1120): C01BC77BB1F9985B0DE82CDD1D0DD14B...
Для сравнения: тот же клиент с -groups X25519 отправляет ClientHello размером 313 байт. Гибрид добавляет примерно 1,2 КБ в каждом направлении, и именно этот размер вызывает большинство проблем совместимости (см. ниже).
От двух секретов к одному расписанию ключей
Каждая сторона выполняет две независимые операции:
- Клиент генерирует пару ключей ML-KEM-768 и пару ключей X25519 и отправляет оба открытых ключа.
- Сервер выполняет инкапсуляцию с ключом ML-KEM и получает 32-байтный секрет и 1088-байтный шифртекст. Кроме того, он выполняет X25519 со своим эфемерным ключом, после чего отправляет шифртекст и свой открытый ключ X25519.
- Клиент выполняет декапсуляцию шифртекста, получая тот же секрет ML-KEM, и завершает X25519 на своей стороне.
- Обе стороны объединяют результаты в 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, вместо группы на основе черновика Kyber | Google 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, Безопасность платформ |
| OpenSSL | 3.5.0: гибридные группы в списке по умолчанию, key share по умолчанию — X25519MLKEM768 + X25519 | NEWS.md |
Go crypto/tls | Go 1.24, если Config.CurvePreferences равно nil. В Go 1.26 добавлены два гибрида на кривых NIST | Go 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 -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 -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():
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)
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:
--- 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:
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».
Если вы столкнулись с этой проблемой:
- Найдите устройство, которое сбрасывает соединение. Сравните
openssl s_client -groups X25519со списком групп по умолчанию на одном и том же сетевом пути. Если сбой происходит только с большим ClientHello, проблема в размере, а не в сертификате. - Обновите или замените это устройство. Если отключить гибрид на клиентах, защита пропадёт для всех соединений, включая те, что работали.
- Используйте переключатели на стороне клиента только как временное решение. В 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.