[about] [index] [prev] [2021-12-03 20:02:06+03:00] [8a63bd890106f79fa53e52ba518c1a2873095eac]
Topics: [nncp]

NNCP развитие и использование людьми

https://jb55.com/log/2021-11-06-airgapped-bitcoin-node-nncp.html
https://blog.taoetc.org/how_to_publish_a_static_site_over_nncp/index.html
Чисто случайно (никакого поиска относящегося к NNCP не делал), ходя по
каким-то ссылкам, внезапно увидел что люди используют NNCP для таких
вещей как airgap BitCoin нода и в качестве транспорта для публикации в
блоги. Кто-то git фигачит поверх NNCP. Приятно что у всех без проблем
получается это собрать из tarball с использованием минималистичного
redo. И что авторы считают что NNCP легко пользоваться. Да и прям
буквально сейчас NNCP упомянут в рассылке обсуждения Gemini протокола и
тоже есть интересующиеся.

Когда я делал очередное изменение шифрованных пакетов
(9dbbfb48af71d290a67a389117411ded7ecc11a6), то подумал про себя что
забавно было бы наблюдать за прогрессом проекта и его изменений форматов
пакетов. Это же ведь и самый долгоживущий более-менее большой проект.
Например на какой-нибудь GoVPN я окончательно плюнул
(9ed0a1b3edd6548e2f9a8b8dadca4975c2bce969).

Простой пакет:

* v1: изначально состоял из:
  TYPE || PATHLEN || PATH || PAYLOAD
* v2: дальше к нему добавился приоритет:
  TYPE || NICE || PATHLEN || PATH || PAYLOAD
* v3: структура осталась прежней. Вместо zlib-сжатия стал использоваться
  Zstandard. Сейчас вот смотрю и понимаю что можно было бы просто ввести
  новый TYPE полезной нагрузки.

Типы простых пакетов:

* изначально были FILE, FREQ (file request), MAIL, TRNS (transitional)
* позже MAIL поменялся на EXEC, оставаясь обратно совместимым по
  формату. NNCP стал не только почту посылать через вызов sendmail, но и
  любую другу команду. Действительно, зачем надо было так себя
  ограничивать? Но, видимо, потому что боялся того что было в UUCP, где
  даже передача файлов превращалась в вызов "cp" команды, а хочется
  эффективности и простоты
* позже узнал что люди применяются nncp-exec для посылки отнюдь не
  маленьких простых файлов, а вообще всякой бинарщины. Как минимум, для
  которой форсированное сжатие EXEC-пакетов будет даже вредным. Появился
  EXEC-FAT тип пакетов, где просто навсего не применяется Zstandard
* ну и относительно недавно появились AREA пакеты, для multicast
  рассылки FILE и EXEC(-FAT)

Sync protocol вообще не поменялся с момента создания. Пакеты имеют тип
и, зависимое от него, тело, всё фиксированного размера. Возможные типы:
    HALT
    PING
    INFO || NICE || SIZE || HASH
    FREQ || HASH || OFFSET
    FILE || HASH || OFFSET || PAYLOAD
    DONE || HASH

Пакеты для multicast discovery появились недавно и вообще состоят ровно
из одного (кроме магического числа) из идентификатора отправителя.

Формат еблобов (EBlob, Encrypted Blob) по сути оставался прежним, но
из-за изменения алгоритмов шифрования для зашифрованных пакетов, заодно
менял и шифрование в нём, поэтому три версии.
    S || T || P || SALT || BLOB
Где S, T, P это параметры Balloon функции хэширования пароля, соль --
просто рандом, ну а BLOB это зашифрованный на выработанном ключе, кусок
данных. Команда nncp-cfgenc может использоваться для любых данных, не
только конфигов, но в NNCP штатно используется только для них.

Формат .nncp.meta пакетов, описывающих .nncp.chunk файлы, по сути тоже
не менялся, но версия один раз поменялась из-за глобальной замены хэш
функции. Всё очень тривиально:
    FILESIZE || CHUNKSIZE || HASH0 || HASH1 || ...

AREA пакеты имеют своё выделенное магическое число, но формат самого
пакета идентичен зашифрованному пакету.

Наконец зашифрованные пакеты:

* v1: первая версия:
    NICE || SENDER || EPUB || SIGN ||
        SIZE || MAC ||
            CIPHERTEXT || MAC || JUNK
    SIGN = sign(NICE || RCPT || SENDER || EPUB
  Использовала Twofish в режиме CTR, BLAKE2b-256-MAC для MAC.
  SIZE зашифрован и для него есть отдельный MAC.
  Четыре ключа (шифрование+MAC для SIZE и полезной нагрузки)
  вырабатывались HKDF-Blake2b-256.
  AES принципиально не хотелось. Почему сразу не выбрал Salsa20
  какой-нибудь? Совершенно не могу вспомнить.

* v2: в заголовке появилось RCPT поле, явно указывающее получателя:
    NICE || SENDER || RCPT || EPUB || SIGN || ...
  Было ли недоработкой отсутствие RCPT прежде? Скорее да. Ничего
  серьёзного бы не произошло, но неприятно видеть пакеты по какой-то
  причине попавшие в spool, но мы их не можем дешифровать.

* v3: Twofish заменён ChaCha20.HKDF-BLAKE2b-256 заменён BLAKE2Xb.
  Минус два криптографических примитива (ChaCha20 используется в SP
  протоколе). Данные разбиваются на 128KiB блоки, шифруемые друг от
  друга независимо, используя номер блока в качестве счётчика, nonce-а
  для ChaCha20.

* v4: BLAKE2b-MAC-256 заменён на Poly1305 и теперь каждый 128KiB блок
  аутентифицируется независимо от других. Это увеличивает суммарный
  размер шифротекста, так как для каждых 128KiB добавляются 128-бит
  Poly1305, но зато существенно упрощает код, и, самое главное то, блок
  сначала дешифруется/аутентифицируется, а уж только потом его выход
  подаётся например на вход внешней EXEC команды.
  Считать ли недоработкой прежний формат? То что в EXEC подаются
  неаутентифицированные данные -- ну это уже проблема вышестоящего
  уровня, аналогичная "gpg -d < | cmd". А не использование Poly1305 всё
  же уменьшало overhead. Мизерный, но всё равно в общем-то избыточный.

* v5: масштабная замена BLAKE2b-256 на BLAKE3 и MTH (Merkle Tree
  Hashing). Содержимое самого пакета это повлияло только заменой
  BLAKE2b KDF функции на BLAKE3. BLAKE2b XOF использовался для создания
  padding-а -- теперь BLAKE3 XOF.

  BLAKE3 на момент создания NNCP ещё не существовал. Когда появился и я
  проникся скоростью его работы -- для меня это ещё не было поводом
  замены BLAKE2b, реализации которого, вообще-то местами могут быть
  сравнимы с BLAKE3. Да и вообще скорость настолько высокая, что не
  проблема.

  Но главное "изобретение" это MTH, который позволяет частями вычислять
  хэш пакета. Главное для чего это делалось: не читать докачиваемый в
  online режиме файл полностью от и до, а после скачивания только
  прочитать его начало до момента начала докачивания. Ведь данные при
  скачивании же всё равно через нас прошли -- хотелось бы сразу сделать
  над ними вычисления и забыть. Ведь файлы могут быть на практике в
  сотни гигабайт: обломались на скачивании первого килобайта, дальше
  скачиваем оставшиеся сотни гигабайт и снова с нуля будем считывать их
  с диска? С MTH это не нужно. Ну а раз это в любом случае делает формат
  хэша над всем пакетом обратно несовместимым, то можно и низлежащий
  алгоритм заменить (BLAKE2b на BLAKE3).

* v6: потоковый формат шифрования.
  SIZE поле отсутствует, но каждый 128KiB блок может быть зашифрован
  одним из двух ключей. В зависимости от ключа, определяется наличие в
  начале блока метаинформации о полном размере пакета и pad-а. Теперь
  временные файлы не нужны вообще, прежде использованные при получении
  данных из stdin-а чтобы узнать их итоговый размер.

[leave comment]