docs: document DH-inclusive transport keys / forward secrecy + trial rate-limit

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).
This commit is contained in:
ospab 2026-07-11 22:01:40 +03:00
parent a9509a235d
commit 4ac2e79e14
2 changed files with 32 additions and 7 deletions

View File

@ -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).

View File

@ -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).