Files
Serdtse-Sysoly/finals/nullroute-kem-crypto-easy/WRITEUP.md
T
2026-09-17 00:50:07 +03:00

3.5 KiB
Raw Blame History

NullRoute KEM

Crypto easy

В раздатке есть capsule.json с отклонённой KEM-капсулой, клиент совместимости kem_client.py и описание формата. Клиент умеет расшифровывать заметки для невалидных капсул. Проверяем, как он вычисляет общий секрет.

Решение

В decapsulate() есть две ветки. Валидная капсула должна иметь длину 52 байта, префикс NRK1 и правильный HMAC-тег. Тогда секрет вычисляется как sha256(b"valid-shared:" + ct). Любая ошибка ведёт к одной константе:

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 включаем следующие данные.

b"NRK-AEAD-v1" + aad + nonce + ciphertext

Если тег сошёлся, генерируем поток блоками по 32 байта:

block = hmac_sha256(
    key, b"nullroute-stream" + nonce + counter.to_bytes(4, "big")
)

Повторяем начальное значение и порядок увеличения счётчика из decrypt_capsule(), склеиваем блоки и обрезаем поток до длины шифротекста. XOR возвращает операторскую заметку с флагом. Чтобы сохранить порядок вывода ключа, проверки тега и генерации потока, используем decrypt_capsule() из выданного клиента. Загружаем модуль и передаём ему разобранный JSON капсулы.

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())

Солвер.

Флаг

caplag{kem_reject_lane_used_the_same_secret}