From 4ac2e79e14ead4d8669c0ffddeeb6aa125a2125d Mon Sep 17 00:00:00 2001 From: ospab Date: Sat, 11 Jul 2026 22:01:40 +0300 Subject: [PATCH] docs: document DH-inclusive transport keys / forward secrecy + trial rate-limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reflect the crypto hardening in the EN/RU specification: - Section 6: transport keys now come from Noise Split() over the chaining key ck (includes the ee DH secret), giving forward secrecy; added the rationale for why keys must NOT come from the handshake hash h, and the wire-version-5 gate. - Section 8: documented the handshake-trial CPU-DoS defense (per-key secret/marker caching + trial-path token bucket). - Corrected the handshake replay window (±300s / 5min, was mis-stated as ±30s) and PSK derivation (HKDF-SHA256). --- docs/en/specification.md | 18 +++++++++++++++--- docs/ru/specification.md | 21 +++++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/docs/en/specification.md b/docs/en/specification.md index c24e0be..95027f7 100644 --- a/docs/en/specification.md +++ b/docs/en/specification.md @@ -90,11 +90,22 @@ Because the `Nonce` is unique per packet, the mask is cryptographically independ OSTP executes a Noise Protocol Framework exchange utilizing the `Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s` pattern. -1. The Registration Key (`access_key`) is converted to a 32-octet strong pre-shared key (PSK) via SHA-256. +1. The Registration Key (`access_key`) is converted to a 32-octet strong pre-shared key (PSK) via HKDF-SHA-256. 2. The PSK is integrated into the state at pattern position zero, authorizing and encrypting the very first handshaking datagram. -3. Ephemeral Curve25519 key exchange is evaluated to synthesize autonomous symmetric keys for subsequent read/write channels. +3. Ephemeral Curve25519 key exchange (`ee`) is evaluated, and the two directional transport keys are taken from Noise's `Split()` over the final chaining key `ck`. -The initial handshake payload includes a Unix timestamp to mitigate replay attacks. The server enforces a strict ±30-second synchronization window. +> **Forward secrecy.** The transport keys are derived from the chaining key +> `ck`, which absorbs the ephemeral `ee` Diffie-Hellman result. They are **not** +> derived from the Noise handshake hash `h` — `h` only ever absorbs public +> transcript data (ephemeral public keys and on-wire ciphertexts) and never the +> DH secret, so keys derived from it would give an access-key holder the ability +> to decrypt any recorded session. Deriving from `ck` binds each session to its +> ephemeral private keys, which are discarded after the handshake: an adversary +> who later compromises the PSK still cannot decrypt past traffic. This is a +> wire-breaking property gated by the internal protocol version (currently 5); +> peers on an older version derive different keys and cannot interoperate. + +The initial handshake payload includes a Unix timestamp to mitigate replay attacks. The server enforces a ±300-second (5-minute) synchronization window and additionally records accepted handshakes in an anti-replay set for that window. --- @@ -126,4 +137,5 @@ The server supports seamless network handoffs (e.g., transitioning from Wi-Fi to * **Nonce Exhaustion:** The Nonce field is 64 bits. Implementations MUST terminate and re-key a session before the Nonce overflows to prevent AEAD keystream reuse. * **Session Exhaustion (DoS):** Servers MUST enforce a strict cap on concurrent sessions (e.g., 1024) and silently drop handshake attempts exceeding this limit to prevent memory exhaustion attacks. +* **Handshake-trial CPU DoS:** Because there is no cleartext key identifier on the wire (a deliberate stealth property), a datagram from an unknown source must be trial-decrypted against every registered key. Servers MUST bound this work: OSTP caches each key's derived secrets and time-windowed junk markers (so a trial is a cheap comparison plus one AEAD attempt per key, not a fresh HKDF/HMAC), and gates the trial path behind a global token bucket (default 100/s) so a spoofed-source flood cannot force unbounded per-packet crypto. The established-session fast path and IP-roaming path are not subject to this bucket. * **Header Authentication:** The header obfuscation mechanism provides privacy, not integrity. Header integrity is mathematically guaranteed by the Poly1305 Authentication Tag, which covers the entire 12-byte header as Additional Authenticated Data (AAD). diff --git a/docs/ru/specification.md b/docs/ru/specification.md index 1f8a32e..a19a011 100644 --- a/docs/ru/specification.md +++ b/docs/ru/specification.md @@ -90,11 +90,23 @@ OSTP поддерживает **внутреннее криптографиче OSTP использует Noise Protocol Framework с паттерном `Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s`. -1. Регистрационный ключ доступа (`access_key`) преобразуется в 32-байтный строгий предварительно распределенный ключ (PSK) через SHA-256. -2. PSK применяется на нулевой позиции паттерна, обеспечивая авторизацию и шифрование самой первой датаграммы рукопожатия (Zero-RTT авторизация). -3. Выполняется эфемерный обмен ключами Curve25519 для создания симметричных ключей передачи данных. +1. Регистрационный ключ доступа (`access_key`) преобразуется в 32-байтный строгий предварительно распределенный ключ (PSK) через HKDF-SHA-256. +2. PSK применяется на нулевой позиции паттерна, обеспечивая авторизацию и шифрование самой первой датаграммы рукопожатия. +3. Выполняется эфемерный обмен ключами Curve25519 (`ee`), и два однонаправленных транспортных ключа берутся из `Split()` протокола Noise над финальным chaining key `ck`. -Первичная полезная нагрузка рукопожатия содержит Unix-отметку времени для защиты от атак повторного воспроизведения (Replay Attacks). Сервер строго контролирует окно синхронизации (±30 секунд). +> **Прямая секретность (Forward Secrecy).** Транспортные ключи выводятся из +> chaining key `ck`, который вбирает результат эфемерного обмена Диффи-Хеллмана +> `ee`. Они **не** выводятся из handshake hash `h` протокола Noise: `h` вбирает +> только публичные данные транскрипта (эфемерные публичные ключи и шифртексты с +> провода) и никогда — сам DH-секрет, поэтому ключи, выведенные из `h`, дали бы +> держателю PSK возможность расшифровать любую записанную сессию. Вывод из `ck` +> привязывает каждую сессию к её эфемерным приватным ключам, которые +> уничтожаются после рукопожатия: злоумышленник, скомпрометировавший PSK позже, +> всё равно не сможет расшифровать прошлый трафик. Это свойство ломает +> совместимость и защищено внутренней версией протокола (сейчас 5): узлы более +> старой версии выводят другие ключи и не могут взаимодействовать. + +Первичная полезная нагрузка рукопожатия содержит Unix-отметку времени для защиты от атак повторного воспроизведения (Replay Attacks). Сервер контролирует окно синхронизации (±300 секунд, 5 минут) и дополнительно фиксирует принятые рукопожатия в множестве защиты от повтора на время этого окна. --- @@ -119,4 +131,5 @@ OSTP обеспечивает надежную доставку поверх UDP * **Исчерпание Nonce:** Поле Nonce имеет размер 64 бита. Реализации ОБЯЗАНЫ разрывать сессию до переполнения Nonce, чтобы предотвратить катастрофическое повторное использование гаммы AEAD-шифра. * **DDoS и исчерпание ресурсов:** Серверы ДОЛЖНЫ применять жесткий лимит на количество одновременных сессий (например, 1024) и молча отбрасывать запросы на рукопожатие при превышении лимита, предотвращая атаки на исчерпание памяти. +* **CPU-DoS на пути перебора рукопожатия:** Поскольку на проводе нет открытого идентификатора ключа (намеренное свойство скрытности), датаграмму от неизвестного источника приходится пробно расшифровывать каждым зарегистрированным ключом. Серверы ОБЯЗАНЫ ограничивать эту работу: OSTP кэширует производные секреты каждого ключа и его junk-маркеры для текущего временно́го окна (поэтому одна попытка — это дешёвое сравнение плюс одна попытка AEAD на ключ, а не новые HKDF/HMAC), и ограничивает путь перебора глобальным token bucket (по умолчанию 100/с), так что флуд с подменённых адресов не может навязать неограниченную криптографию на пакет. Быстрый путь установленных сессий и путь IP-роуминга под этот лимит не попадают. * **Целостность заголовка:** Механизм маскирования обеспечивает только скрытность, а не целостность. Целостность заголовков математически гарантируется 16-байтным тегом аутентификации Poly1305, который покрывает 12-байтный заголовок как присоединенные данные (AAD).