NullRoute KEM

Crypto easy

В раздатке есть `capsule.json` с отклонённой KEM-капсулой, клиент совместимости `kem_client.py` и описание формата. Клиент умеет расшифровывать заметки для невалидных капсул. Проверяем, как он вычисляет общий секрет. ## Решение В `decapsulate()` есть две ветки. Валидная капсула должна иметь длину 52 байта, префикс `NRK1` и правильный HMAC-тег. Тогда секрет вычисляется как `sha256(b"valid-shared:" + ct)`. Любая ошибка ведёт к одной константе: ```python from hashlib import sha256 shared_secret = sha256( b"NullRoute/KEM-reject/fallback-shared-secret/v1" ).digest() ``` Капсула из раздатки отклонена, поэтому попадает именно сюда. Общий секрет известен из исходника, а все остальные параметры лежат в JSON: | Поле | Роль | |---|---| | `salt_hex` | Соль для вывода ключа | | `nonce_hex` | Nonce потока и проверки тега | | `aad` | Дополнительные аутентифицируемые данные | | `ciphertext_hex`, `tag_hex` | Зашифрованная заметка и её тег | Повторяем `decrypt_capsule()`. Поля с суффиксом `_hex` переводим в байты, `aad` кодируем так же, как клиент. Через HKDF на HMAC-SHA256 выводим из `shared_secret` и соли `salt_hex` 32-байтный ключ с `info = b"NullRoute reject lane note key"`. Этим ключом считаем HMAC-SHA256 и сверяем его первые 16 байт с тегом заметки. В HMAC включаем следующие данные. ```text b"NRK-AEAD-v1" + aad + nonce + ciphertext ``` Если тег сошёлся, генерируем поток блоками по 32 байта: ```text block = hmac_sha256( key, b"nullroute-stream" + nonce + counter.to_bytes(4, "big") ) ``` Повторяем начальное значение и порядок увеличения счётчика из `decrypt_capsule()`, склеиваем блоки и обрезаем поток до длины шифротекста. XOR возвращает операторскую заметку с флагом. Чтобы сохранить порядок вывода ключа, проверки тега и генерации потока, используем `decrypt_capsule()` из выданного клиента. Загружаем модуль и передаём ему разобранный JSON капсулы. ```python from pathlib import Path import importlib.util import json spec = importlib.util.spec_from_file_location("kem_client", "public/kem_client.py") client = importlib.util.module_from_spec(spec) spec.loader.exec_module(client) capsule = json.loads(Path("public/capsule.json").read_text()) print(client.decrypt_capsule(capsule).decode()) ``` [Солвер](solve/solve.py). ## Флаг `caplag{kem_reject_lane_used_the_same_secret}`