Files
2026-09-17 00:50:07 +03:00

64 lines
3.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<h1 align="center">NullRoute KEM</h1>
<p align="center">
<img src="https://img.shields.io/badge/category-Crypto-blueviolet" alt="Crypto"/>
<img src="https://img.shields.io/badge/difficulty-easy-brightgreen" alt="easy"/>
</p>
В раздатке есть `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}`