README | 2 +- README.RU | 35 +++++++++++++++++++++++++++++++++++ THANKS | 5 ----- VERSION | 2 +- common.mk | 26 +++++++++++++++++--------- doc/about.ru.texi | 44 ++++++++++++++++++++++++++++++++++++++++++++ doc/about.texi | 37 +++++++++++++++++++++++++++++++++++++ doc/call.texi | 6 +++--- doc/cfg.texi | 10 ++++++++++ doc/chunked.texi | 45 +++++++++++++++++++++++++++++++++++++++++++++ doc/cmds.texi | 211 +++++++++++++++++++++++++++++++++++++++++------------ doc/comparison.ru.texi | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/comparison.texi | 31 +++++++++++++++++++------------ doc/download.texi | 7 ++++++- doc/eblob.texi | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/index.texi | 40 +++++++--------------------------------- doc/install.texi | 3 +-- doc/news.ru.texi | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/news.texi | 44 ++++++++++++++++++++++++++++++++++++++++++-- doc/pkt.texi | 9 ++++----- doc/russian.texi | 14 ++++++++++++++ doc/sources.texi | 4 +--- doc/sp.texi | 16 ++++++++-------- doc/usecases.ru.texi | 298 +++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/usecases.texi | 61 +++++++++++++++++++++++++++++------------------------ doc/workflow.texi | 2 +- makedist.sh | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++--- news_and_install.sh | 27 --------------------------- ports/nncp/Makefile | 18 +++++++++--------- ports/nncp/files/nncp.newsyslog.conf.in => ports/nncp/files/nncp.newsyslog.conf.sample.in | 0 ports/nncp/files/pkg-deinstall.in | 8 ++------ ports/nncp/files/pkg-install.in | 5 +++++ src/cypherpunks.ru/nncp/cfg.go | 52 +++++++++++++++++++++++++++++++++++++++++----------- src/cypherpunks.ru/nncp/chunked.go | 33 +++++++++++++++++++++++++++++++++ src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/cypherpunks.ru/nncp/cmd/nncp-file/main.go | 32 ++++++++++++++++++++++++-------- src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go | 4 ++-- src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go | 4 ++-- src/cypherpunks.ru/nncp/cmd/nncp-mincfg/main.go => src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go | 2 +- src/cypherpunks.ru/nncp/cmd/nncp-newcfg/main.go => src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go | 2 +- src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go | 355 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go | 5 +++-- src/cypherpunks.ru/nncp/eblob.go | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/cypherpunks.ru/nncp/humanizer.go | 19 +++++++++++++++++++ src/cypherpunks.ru/nncp/node.go | 2 ++ src/cypherpunks.ru/nncp/toss.go | 20 +++++++++++++++++++- src/cypherpunks.ru/nncp/tx.go | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++--- supplementary_files.sh | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/README b/README index 7f888d44c926b4870ebd3c9e67d8ccde03315baadb08b5c60703bd81ddeda980..bf62e3b5440497304473fa4113c3ec02c4593c28f44eb3099d9a7364164d37c0 100644 --- a/README +++ b/README @@ -24,6 +24,6 @@ patches to nncp-devel mailing list: https://lists.cypherpunks.ru/pipermail/nncp-devel/ Development Git source code repository currently is located here: -http://git.cypherpunks.ru/cgit.cgi/nncp.git/ +https://git.cypherpunks.ru/cgit.cgi/nncp.git/ For further information please read either doc/nncp.info or doc/nncp.texi. diff --git a/README.RU b/README.RU new file mode 100644 index 0000000000000000000000000000000000000000..dabfd57544e9e606ad857534605aa8ceb0f34ae5939213b814e48e7b507fc0b3 --- /dev/null +++ b/README.RU @@ -0,0 +1,35 @@ +NNCP (Node to Node copy) это набор утилит упрощающий безопасный обмен +файлами и почтой в режиме сохранить-и-переслать. + +Эти утилиты предназначены помочь с построением одноранговых сетей +небольшого размера (дюжины узлов), в режиме друг-к-другу (F2F) со +статической маршрутизацией для безопасной надёжной передачи файлов, +запросов на передачу файлов и Интернет почты по принципу +выстрелил-и-забыл. Все пакеты проверяются на целостность, шифруются по +принципу точка-точка (E2EE), аутентифицируются известными публичными +ключами участников. Луковичное (onion) шифрование применяется ко всем +ретранслируемым пакетам. Каждый узел выступает одновременно в роли +клиента и сервера, может использовать как push, так и poll модель +поведения. + +Поддержка из коробки offline флоппинета, тайников для сброса информации +(dead drop) и компьютеров с "воздушным зазором" (air-gap). Но также +существует и online TCP демон с полнодуплексной возобновляемой передачей +данных. + +NNCP это свободное программное обеспечением: условия распространения +находятся в файле COPYING. Оно должно работать на всех POSIX-совместимых +системах. Лёгкая интеграция с существующими SMTP серверами. Единственный +конфигурационный YAML файл. + +Домашняя страница: http://www.nncpgo.org/ + +Пожалуйста все вопросы касающиеся использования NNCP, отчёты об ошибках +и патчи отправляйте в nncp-devel почтовую рассылку: +https://lists.cypherpunks.ru/pipermail/nncp-devel/ + +Исходный код для разработчика находится в Git репозитории: +https://git.cypherpunks.ru/cgit.cgi/nncp.git/ + +Для дополнительной информации пожалуйста читайте или doc/nncp.info +или doc/nncp.texi. diff --git a/THANKS b/THANKS deleted file mode 100644 index 58ecd3295d633e102b63a9d1f76cd2a4984dcf45322071ed0c4f83f30a758ec6..0000000000000000000000000000000000000000 --- a/THANKS +++ /dev/null @@ -1,5 +0,0 @@ -There are people deserving to be thanked for helping this project: - -* Shawn K. Quinn for his descriptive - instructions about building NNCP under Ubuntu GNU/Linux distributions - and bug reports diff --git a/VERSION b/VERSION index 3de37169f6c8e4050f9dd746f327e07d86078365055b53f2bbdbcf96c0202654..acb250b01d4b5202642788572f9459effc1bcba91b405e9c6146f1dbd0b80e6f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6 +0.7 diff --git a/common.mk b/common.mk index 8b97a633c3f2b7ad83c7375ba245440f475206d37c02e0b78f04ce95d032304c..1f56c08ce283d4d6eb4c553d79ba3b6bd5c7a82c91c56eed40cce475a6efef98 100644 --- a/common.mk +++ b/common.mk @@ -19,15 +19,17 @@ ALL = \ nncp-call \ nncp-caller \ + nncp-cfgenc \ + nncp-cfgmin \ + nncp-cfgnew \ nncp-check \ nncp-daemon \ nncp-file \ nncp-freq \ nncp-log \ nncp-mail \ - nncp-mincfg \ - nncp-newcfg \ nncp-pkt \ + nncp-reass \ nncp-rm \ nncp-stat \ nncp-toss \ @@ -41,6 +43,15 @@ nncp-caller: GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-caller +nncp-cfgenc: + GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgenc + +nncp-cfgmin: + GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgmin + +nncp-cfgnew: + GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgnew + nncp-check: GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-check @@ -59,14 +70,11 @@ nncp-mail: GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-mail -nncp-mincfg: - GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-mincfg - -nncp-newcfg: - GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-newcfg - nncp-pkt: GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-pkt + +nncp-reass: + GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-reass nncp-rm: GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-rm @@ -99,7 +107,7 @@ mkdir -p $(INFODIR) cp -f doc/nncp.info $(INFODIR) chmod 644 $(INFODIR)/nncp.info mkdir -p $(DOCDIR) - cp -f -L AUTHORS NEWS README THANKS $(DOCDIR) + cp -f -L AUTHORS NEWS NEWS.RU README README.RU THANKS $(DOCDIR) chmod 644 $(DOCDIR)/* install-strip: install diff --git a/doc/about.ru.texi b/doc/about.ru.texi new file mode 100644 index 0000000000000000000000000000000000000000..86b673949eafaa0bf517f53fb61891450c438d285f37325807508f2715634405 --- /dev/null +++ b/doc/about.ru.texi @@ -0,0 +1,44 @@ +@node Об утилитах +@section Подробнее об утилитах NNCP + +NNCP (Node to Node copy) это набор утилит упрощающий безопасный обмен +файлами и почтой в режиме сохранить-и-переслать. + +Эти утилиты предназначены помочь с построением однораговых сетей +небольшого размера (дюжины узлов), в режиме +@url{https://ru.wikipedia.org/wiki/Friend-to-friend, друг-к-другу} (F2F) +со статической маршрутизацией для безопасной надёжной передачи файлов, +запросов на передачу файлов и Интернет почты по принципу +выстрелил-и-забыл. Все пакеты проверяются на целостность, шифруются по +принципу @url{https://en.wikipedia.org/wiki/End-to-end_encryption, +точка-точка}, аутентифицируются известными публичными ключами +участников. +@url{https://ru.wikipedia.org/wiki/%D0%9B%D1%83%D0%BA%D0%BE%D0%B2%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F, Луковичное} +(onion) шифрование применяется ко всем ретранслируемым пакетам. Каждый +узел выступает одновременно в роли клиента и сервера, может использовать +как push, так и poll модель поведения. + +Поддержка из коробки offline +@url{https://ru.wikipedia.org/wiki/%D0%A4%D0%BB%D0%BE%D0%BF%D0%BF%D0%B8%D0%BD%D0%B5%D1%82, +флоппинета}, +@url{https://ru.wikipedia.org/wiki/%D0%A2%D0%B0%D0%B9%D0%BD%D0%B8%D0%BA, +тайников} для сброса информации (dead drop) и компьютеров с +@url{https://ru.wikipedia.org/wiki/%D0%92%D0%BE%D0%B7%D0%B4%D1%83%D1%88%D0%BD%D1%8B%D0%B9_%D0%B7%D0%B0%D0%B7%D0%BE%D1%80_(%D1%81%D0%B5%D1%82%D0%B8_%D0%BF%D0%B5%D1%80%D0%B5%D0%B4%D0%B0%D1%87%D0%B8_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85), +воздушным зазором} (air-gap). Но также существует и online TCP демон с +полнодуплексной возобновляемой передачей данных. + +Узнайте о возможных @ref{Сценарии использования, сценариях использования}! + +NNCP это @url{https://www.gnu.org/philosophy/pragmatic.ru.html, +копилефт} @url{https://www.gnu.org/philosophy/free-sw.ru.html, свободное +программное обеспечение}: лицензировано под условиями +@url{https://www.gnu.org/licenses/gpl-3.0.ru.html, GNU GPLv3+}. Оно +должно работать на всех @url{https://ru.wikipedia.org/wiki/POSIX, +POSIX}-совместимых системах. Лёгкая интеграция с существующими +@url{https://ru.wikipedia.org/wiki/SMTP, SMTP} серверами. Единственный +конфигурационный @url{http://yaml.org/, YAML} файл. + +Зачем создавать ещё одно решение с принципом сохранить-и-переслать когда +уже существуют UUCP, FTN и даже SMTP? Посмотрите @ref{Сравнение, сравнение}! +Простота, криптографическая безопасность, совместимость с флоппинетом и +лёгкая интеграция с существующими SMTP серверами -- вот и причины. diff --git a/doc/about.texi b/doc/about.texi new file mode 100644 index 0000000000000000000000000000000000000000..b7f824692c4c40215120a1b96d906548842437cf09d45698c30bc5d0dd9f67b3 --- /dev/null +++ b/doc/about.texi @@ -0,0 +1,37 @@ +NNCP (Node to Node copy) is a collection of utilities simplifying +secure store-and-forward files and mail exchanging. + +See also this page @ref{Об утилитах, on russian}. + +This utilities are intended to help build up small size (dozens of +nodes) ad-hoc @url{https://en.wikipedia.org/wiki/Friend-to-friend, +friend-to-friend} (F2F) statically routed +@url{https://en.wikipedia.org/wiki/Darknet, darknet} networks for +fire-and-forget secure reliable files, file requests and Internet mail +transmission. All packets are integrity checked, +@url{https://en.wikipedia.org/wiki/End-to-end_encryption, end-to-end} +encrypted, explicitly authenticated by known participants public keys. +@url{https://en.wikipedia.org/wiki/Onion_routing, Onion encryption} is +applied to relayed packets. Each node acts both as a client and server, +can use push and poll behaviour model. + +Out-of-box offline @url{https://en.wikipedia.org/wiki/Sneakernet, +sneakernet/floppynet}, @url{https://en.wikipedia.org/wiki/Dead_drop, +dead drops} and @url{https://en.wikipedia.org/wiki/Air_gap_(networking), +air-gapped} computers support. But online TCP daemon with full-duplex +resumable data transmission exists. + +Look for possible @ref{Use cases, use cases}! + +NNCP is @url{https://www.gnu.org/philosophy/pragmatic.html, copylefted} +@url{https://www.gnu.org/philosophy/free-sw.html, free software} +licenced under @url{https://www.gnu.org/licenses/gpl-3.0.html, GNU GPLv3+}. +It should work on all @url{https://en.wikipedia.org/wiki/POSIX, +POSIX}-compatible systems. Easy integration with existing +@url{https://en.wikipedia.org/wiki/SMTP, SMTP} servers. Single +@url{http://yaml.org/, YAML} configuration file. + +Why create yet another store-and-forward solution when UUCP, FTN and +even SMTP exists? Look in @ref{Comparison, comparison} section! +Simplicity, cryptographic security, sneakernet compatibility and easy +integration with current SMTP servers are the reasons. diff --git a/doc/call.texi b/doc/call.texi index eeb26581844089c5fbc625f2a5564413ffd208101617239797d82d39edbbf15d..de2f665fc73b45bc2c5083b9147a5b688bc594ee62ed9ddacfd8d439084bbae0 100644 --- a/doc/call.texi +++ b/doc/call.texi @@ -8,16 +8,16 @@ @verbatim calls: - - cron: "*/1 * * * 0-4" + cron: "*/1 * * * MON-FRI" onlinedeadline: 3600 nice: 64 - - cron: "30 * * * 5-6" + cron: "30 * * * SAT,SUN" onlinedeadline: 1800 maxonlinetime: 1750 nice: 64 - - cron: "0 * * * 5-6" + cron: "0 * * * SAT,SUN" xx: rx addr: lan @end verbatim diff --git a/doc/cfg.texi b/doc/cfg.texi index 78e15daae21f37401297671476c6c9affb7e0822effd5ec07f13d18ca23b97f9..09f2f4a75a91edb21419fe4be9662d35f5a495f7c3c8ee42aff571827c1079f8 100644 --- a/doc/cfg.texi +++ b/doc/cfg.texi @@ -49,6 +49,8 @@ exchpub: WFLMZ...B7NHA signpub: GTGXG...IE3OA sendmail: [/usr/sbin/sendmail] freq: /home/bob/pub + freqchunked: 1024 + freqminsize: 2048 via: [alice] @end verbatim @@ -98,6 +100,14 @@ @anchor{CfgFreq} @item freq Full path to directory from where file requests will queue files for transmission. May be omitted to forbid freqing from that node. + +@item freqchunked +If set, then enable @ref{Chunked, chunked} file transmission during +freqing. This is the desired chunk size in KiBs. + +@item freqminsize +If set, then apply @ref{OptMinSize, -minsize} option during file +transmission. @item via An array of node identifiers that will be used as a relay to that node. diff --git a/doc/chunked.texi b/doc/chunked.texi new file mode 100644 index 0000000000000000000000000000000000000000..e04daeaa3abcb8c55625a66efd552bfef8033f61d0b4a50023de26f87dc91412 --- /dev/null +++ b/doc/chunked.texi @@ -0,0 +1,45 @@ +@node Chunked +@unnumbered Chunked files + +There is ability to transfer huge files with splitting them into smaller +chunks. Each chunk is treated like a separate file, producing separate +outbound packet unrelated with other ones. + +This is useful when your removable storage device has smaller capacity +than huge file's size. You can transfer those chunks on different +storage devices, and/or at different time, reassembling the whole packet +on the destination node. + +Splitting is done with @ref{nncp-file, nncp-file -chunked} command and +reassembling with @ref{nncp-reass} command. + +Chunked @file{FILE} produces @file{FILE.nncp.meta}, +@file{FILE.nncp.chunk0}, @file{FILE.nncp.chunk1}, ... files. All +@file{.nncp.chunkXXX} can be concatenated together to produce original +@file{FILE}. + +@file{.nncp.meta} contains information about file/chunk +size and their hash checksums. This is +@url{https://tools.ietf.org/html/rfc4506, XDR}-encoded structure: + +@verbatim ++------------------------------+---------------------+ +| MAGIC | FILESIZE | CHUNKSIZE | HASH0 | HASH1 | ... | ++------------------------------+---------------------+ +@end verbatim + +@multitable @columnfractions 0.2 0.3 0.5 +@headitem @tab XDR type @tab Value +@item Magic number @tab + 8-byte, fixed length opaque data @tab + @verb{|N N C P M 0x00 0x00 0x01|} +@item File size @tab + unsigned hyper integer @tab + Whole reassembled file's size +@item Chunk size @tab + unsigned hyper integer @tab + Size of each chunk (except for the last one, that could be smaller) +@item Checksums @tab + variable length array of 32 byte fixed length opaque data @tab + BLAKE2b-256 checksum of each chunk +@end multitable diff --git a/doc/cmds.texi b/doc/cmds.texi index ca75fe63cf2272a412767eeec262858d7776b61bfbe75376c0d68a4401bb178a..12ecf3acad63f2ca39817ed66421624787936a1ecd01e022d63fdba90b64770c 100644 --- a/doc/cmds.texi +++ b/doc/cmds.texi @@ -5,14 +5,16 @@ Nearly all commands have the following common options: @table @option @item -cfg - Path to configuration file. May be overrided by @env{NNCPCFG} - environment variable. + Path to configuration file. May be overridden by @env{NNCPCFG} + environment variable. If file file is an encrypted @ref{EBlob, + eblob}, then ask for passphrase to decrypt it first. @item -debug Print debug messages. Normally this option should not be used. @item -minsize - Minimal required resulting packet size. For example if you send 2 - KiB file and set @option{-minsize 4096}, then resulting packet will - be 4 KiB (containing file itself and some junk). + @anchor{OptMinSize} + Minimal required resulting packet size, in KiBs. For example if you + send 2 KiB file and set @option{-minsize 4}, then resulting packet + will be 4 KiB (containing file itself and some junk). @item -nice Set desired outgoing packet @ref{Niceness, niceness level}. 1-255 values are allowed. @@ -81,6 +83,72 @@ file is renamed from @file{.part} one and when you rerun @command{nncp-call} again, remote node will receive completion notification. +@node nncp-cfgenc +@section nncp-cfgenc + +@verbatim +% nncp-cfgmin [options] [-s INT] [-t INT] [-p INT] cfg.yaml > cfg.yaml.eblob +% nncp-cfgmin [options] -d cfg.yaml.eblob > cfg.yaml +@end verbatim + +This command allows you to encrypt provided @file{cfg.yaml} file with +the passphrase, producing @ref{EBlob, eblob}, to safely keep your +configuration file with private keys. This utility was written for users +who do not want (or can not) to use either @url{https://gnupg.org/, +GnuPG} or similar tools. That @file{eblob} file can be used directly in +@option{-cfg} option of nearly all commands. + +@option{-s}, @option{-t}, @option{-p} are used to tune @file{eblob}'s +password strengthening function. Space memory cost (@option{-s}), +specified in number of BLAKE2b-256 blocks (32 bytes), tells how many +memory must be used for hashing -- bigger values are better, but slower. +Time cost (@option{-t}) tells how many rounds/iterations must be +performed -- bigger is better, but slower. Number of parallel jobs +(@option{-p}) tells how many computation processes will be run: this is +the same as running that number of independent hashers and then joining +their result together. + +When invoked for encryption, passphrase is entered manually twice. When +invoked for decryption (@option{-d} option), it is asked once and exits +if passphrase can not decrypt @file{eblob}. + +@option{-dump} options parses @file{eblob} and prints parameters used +during its creation. For example: +@verbatim +% nncp-cfgenc -dump /usr/local/etc/nncp.yaml.eblob +Strengthening function: Balloon with BLAKE2b-256 +Memory space cost: 1048576 bytes +Number of rounds: 16 +Number of parallel jobs: 2 +Blob size: 2494 +@end verbatim + +@node nncp-cfgmin +@section nncp-cfgmin + +@verbatim +% nncp-cfgmin [options] > stripped.yaml +@end verbatim + +Print out stripped configuration version: only path to @ref{Spool, +spool}, path to log file, neighbours public keys are stayed. This is +useful mainly for usage with @ref{nncp-xfer} that has to know only +neighbours, without private keys involving. + +@node nncp-cfgnew +@section nncp-cfgnew + +@verbatim +% nncp-cfgnew [options] > new.yaml +@end verbatim + +Generate new node configuration: private keys, example configuration +file and print it to stdout. You must use this command when you setup +the new node. + +Pay attention that private keys generation consumes an entropy from your +operating system. + @node nncp-check @section nncp-check @@ -90,7 +158,7 @@ @end verbatim Perform @ref{Spool, spool} directory integrity check. Read all files that has Base32-encoded filenames and compare it with recalculated -BLAKE2b hash output of their contents. This supplementary command is +BLAKE2b hash output of their contents. That supplementary command is not used often in practice, if ever. @node nncp-daemon @@ -113,7 +181,7 @@ @node nncp-file @section nncp-file @verbatim -% nncp-file [options] SRC NODE:[DST] +% nncp-file [options] [-chunked INT] SRC NODE:[DST] @end verbatim Send @file{SRC} file to remote @option{NODE}. @file{DST} specifies @@ -124,6 +192,24 @@ This command queues file in @ref{Spool, spool} directory immediately (through the temporary file of course) -- so pay attention that sending 2 GiB file will create 2 GiB outbound encrypted packet. + +If @file{SRC} equals to @file{-}, then create an encrypted temporary +file and copy everything taken from stdin to it and use for outbound +packet creation. Pay attention that if you want to send 1 GiB of data +taken from stdin, then you have to have 2 GiB of disk space for that +temporary file and resulting encrypted packet. You can control where +temporary file will be stored using @env{TMPDIR} environment variable. +Encryption is performed with +@url{https://www.schneier.com/academic/twofish/, Twofish} algorithm, 256 +bit random key, zero IV, in +@url{https://en.wikipedia.org/wiki/Counter_mode#Counter_.28CTR.29, CTR} +mode. + +If @option{-chunked} is specified, then source file will be split +@ref{Chunked, on chunks}. @option{INT} is the desired chunk size in +KiBs. This mode is more CPU hungry. Pay attention that chunk is saved in +spool directory immediately and it is not deleted if any error occurs. +@option{-minsize} option is applied per each chunk. If @ref{CfgNotify, notification} is enabled on the remote side for file transmissions, then it will sent simple letter after successful @@ -166,32 +252,6 @@ side will execute specified @ref{CfgSendmail, sendmail} command with @option{USER}s appended as a command line argument and feed decompressed mail body to that command's stdin. -@node nncp-mincfg -@section nncp-mincfg - -@verbatim -% nncp-mincfg [options] > stripped.yaml -@end verbatim - -Print out stripped configuration version: only path to @ref{Spool, -spool}, path to log file, neighbours public keys are stayed. This is -useful mainly for usage with @ref{nncp-xfer} that has to know only -neighbours, without private keys involving. - -@node nncp-newcfg -@section nncp-newcfg - -@verbatim -% nncp-newcfg [options] > new.yaml -@end verbatim - -Generate new node configuration: private keys, example configuration -file and print it to stdout. You must use this command when you setup -the new node. - -Pay attention that private keys generation consumes an entropy from your -operating system. - @node nncp-pkt @section nncp-pkt @@ -230,6 +290,74 @@ (the whole file, mail message, and so on). @option{-decompress} option tries to zlib-decompress the data from plain packet (useful for mail packets). +@node nncp-reass +@section nncp-reass + +@verbatim +% nncp-reass [options] [-dryrun] [-keep] [-dump] [-stdout] FILE.nncp.meta +% nncp-reass [options] [-dryrun] [-keep] {-all | -node NODE} +@end verbatim + +Reassemble @ref{Chunked, chunked file} after @ref{nncp-toss, tossing}. + +When called with @option{FILE} option, this command will reassemble only +it. When called with @option{-node} option, this command will try to +reassemble all @file{.nncp.meta} files found in @option{NODE}'s +@ref{CfgIncoming, incoming} directory. When called with @option{-all} +option, then cycle through all known nodes to do the same. + +Reassembling process does the following: + +@enumerate +@item Parses @ref{Chunked, @file{.nncp.meta}} file. +@item Checks existence and size of every @file{.nncp.chunkXXX}. +@item Verifies integrity of every chunk. +@item Concatenates all chunks, simultaneously removing them from filesystem. +@end enumerate + +That process reads the whole data twice. Be sure to have free disk +space for at least one chunk. Decrypted chunk files as a rule are saved +in pseudo-random order, so removing them during reassembly process will +likely lead to filesystem fragmentation. Reassembly process on +filesystems with deduplication capability should be rather lightweight. + +If @option{-dryrun} option is specified, then only existence and +integrity checking are performed. + +If @option{-keep} option is specified, then no +@file{.nncp.meta}/@file{.nncp.chunkXXX} files are deleted during +reassembly process. + +@option{-stdout} option outputs reassembled file to stdout, instead of +saving to temporary file with renaming after. This could be useful for +reassembling on separate filesystem to lower fragmentation effect, +and/or separate storage device for higher performance. + +@option{-dump} option prints meta-file contents in human-friendly form. +It is useful mainly for debugging purposes. For example: +@verbatim +Original filename: testfile +File size: 3.8 MiB (3987795 bytes) +Chunk size: 1.0 MiB (1048576 bytes) +Number of chunks: 4 +Checksums: + 0: eac60d819edf40b8ecdacd0b9a5a8c62de2d15eef3c8ca719eafa0be9b894017 + 1: 013a07e659f2e353d0e4339c3375c96c7fffaa2fa00875635f440bbc4631052a + 2: f4f883975a663f2252328707a30e71b2678f933b2f3103db8475b03293e4316e + 3: 0e9e229501bf0ca42d4aa07393d19406d40b179f3922a3986ef12b41019b45a3 +@end verbatim + +@node nncp-rm +@section nncp-rm + +@verbatim +% nncp-rm [options] NODE PKT +@end verbatim + +Remove specified packet (Base32 name) in @option{NODE}'s queues. This +command is useful when you want to remove the packet that is failing to +be processed. + @node nncp-stat @section nncp-stat @@ -265,7 +393,7 @@ @node nncp-xfer @section nncp-xfer @verbatim -% nncp-xfer [options] [-force] [-keep] [-rx|-tx] DIR +% nncp-xfer [options] [-mkdir] [-keep] [-rx|-tx] DIR @end verbatim Search for directory in @file{DIR} containing inbound packets for us and @@ -273,7 +401,7 @@ move them to local @ref{Spool, spool} directory. Also search for known neighbours directories and move locally queued outbound packets to them. This command is used for offline packets transmission. -If @option{-force} option is specified, then outbound neighbour(s) +If @option{-mkdir} option is specified, then outbound neighbour(s) directories will be created. This is useful for the first time usage, when storage device does not have any directories tree. @@ -283,20 +411,9 @@ @option{-rx} option tells only to move inbound packets addressed to us. @option{-tx} option tells exactly the opposite: move only outbound packets. -@ref{nncp-mincfg} could be useful for creating stripped minimalistic +@ref{nncp-cfgmin} could be useful for creating stripped minimalistic configuration file version without any private keys. @file{DIR} directory has the following structure: @file{RECIPIENT/SENDER/PACKET}, where @file{RECIPIENT} is Base32 encoded destination node, @file{SENDER} is Base32 encoded sender node. - -@node nncp-rm -@section nncp-rm - -@verbatim -% nncp-rm [options] NODE PKT -@end verbatim - -Remove specified packet (Base32 name) in @option{NODE}'s queues. This -command is useful when you want to remove the packet that is failing to -be processed. diff --git a/doc/comparison.ru.texi b/doc/comparison.ru.texi new file mode 100644 index 0000000000000000000000000000000000000000..61c9cca45160a1cf1d79173955376d869d34c8c95411431413301d2a37d0eb81 --- /dev/null +++ b/doc/comparison.ru.texi @@ -0,0 +1,119 @@ +@node Сравнение +@section Сравнение с существующими решениями + +Это сравнение @url{https://ru.wikipedia.org/wiki/Uucp, UUCP} +(Unix to Unix copy), @url{https://ru.wikipedia.org/wiki/FTN, FTN} +(@url{https://ru.wikipedia.org/wiki/%D0%A4%D0%B8%D0%B4%D0%BE, FidoNet}) +и @url{https://ru.wikipedia.org/wiki/SMTP, SMTP} (так как это тоже +сохранить-и-переслать режим). + +@multitable @columnfractions 0.40 0.15 0.15 0.15 0.15 +@headitem @tab UUCP @tab FTN @tab NNCP @tab SMTP + +@item Простота настройки @tab Средне @tab Сложно @tab Легко @tab Сложно +@item Передача почты @tab @strong{Да} @tab @strong{Да} @tab @strong{Да} @tab @strong{Да} +@item Передача новостей @tab @strong{Да} @tab @strong{Да} @tab Нет @tab Нет +@item Передача файлов @tab @strong{Да} @tab @strong{Да} @tab @strong{Да} @tab Нет +@item Разбиение файлов на части @tab Нет @tab @strong{Да} @tab @strong{Да} @tab Нет +@item Удалённое исполнение команд @tab @strong{Да} @tab Нет @tab Нет @tab Нет +@item Возобновляемое скачивание @tab @strong{Да} @tab @strong{Да} @tab @strong{Да} @tab Нет +@item Приоритезация пакетов @tab @strong{Да} @tab Нет @tab @strong{Да} @tab Нет +@item Сжатие почты @tab Нет @tab @strong{Да} @tab @strong{Да} @tab Нет +@item Интеграция с SMTP @tab @strong{Да} @tab Нет @tab @strong{Да} @tab N/A +@item Push/poll модель @tab @strong{Обе} @tab @strong{Обе} @tab @strong{Обе} @tab Push +@item DTN @tab @strong{Да} @tab @strong{Да} @tab @strong{Да} @tab Нет +@item Предполагаемый размер сети @tab Дюжины @tab Глобально @tab Дюжины @tab Глобально +@item Маршрутизация @tab Ручное/статичное @tab Федеративное @tab Ручное/статичное @tab Федеративное +@item Поддержка телефонной сети @tab @strong{Да} @tab @strong{Да} @tab Возможно @tab Нет +@item Анонимные участники @tab @strong{Да} @tab Нет @tab Нет @tab @strong{Да} +@item Аутентификация участников @tab PAP @tab PAP/CHAP @tab публичный ключ @tab Нет +@item Шифрование пакетов @tab Нет @tab Нет @tab @strong{Да} @tab Нет +@item Приватность метаданных @tab Нет @tab Нет @tab @strong{Да} @tab Нет +@item Проверка целостности пакетов @tab Нет @tab Нет @tab @strong{Да} @tab Нет +@item Дружелюбность к флоппинету @tab Нет @tab Нет @tab @strong{Да} @tab Нет + +@end multitable + +@table @strong + +@item Простота установки + UUCP относительно легко настраивается несколькими строчками в + нескольких конфигурационных файлах. Но вы вынуждены добавить + дополнительный уровень шифрования и аутентификации для безопасного + обмена данными. + + FTN сложен в настройке, потому-что это совершенно другой мир + программного обеспечения, по-сравнению с Unix-ом. Даже редактор + почты будет какой-нибудь GoldEd, а не обычный почтовый клиент. Более + того, из коробки не предоставляется никакого шифрования и сильной + аутентификации. + + NNCP требует редактирование единственного YAML @ref{Configuration, + конфигурационного файла}. + +@item Передача новостей + SMTP ничего не знает о новостях, NNTP и тому подобному. NNCP тоже не + знает, потому-что на текущий день они уже мало используются. + +@item Передача файлов + SMTP может передавать файлы только в Base64 кодировке -- это очень + не эффективно. + +@item Разбиение файлов на части + FTN программы могут автоматически разбивать огромные файлы на + меньшие части, чтобы собрать их воедино на целевом узле. NNCP тоже + @ref{Chunked, поддерживает} эту возможность, особенно важную когда + дело касается переносных устройств хранения небольшого объёма. + +@item Приоритезация пакетов + UUCP и NNCP сначала будут отправлять пакеты с высоким приоритетом + ("grade" в терминологии UUCP). Ваша почта пройдёт, даже если при + этом в очереди на отправку будут гигабайты файлов. + +@item Интеграция с SMTP + Почтовые серверы типа @url{http://www.postfix.org/, Postfix} + предоставляют документацию и примеры конфигурации для использования + с UUCP. @url{http://www.exim.org/, Exim} и + @url{http://www.sendmail.com/sm/open_source/, Sendmail} тоже + относительно легко могут быть интегрированы с ним. Для использования + с NNCP, просто замените UUCP команды на аналогичные NNCP. + +@item Push/poll модель + С SMTP, вы вынуждены ждать в online режиме когда удалённые участники + отправят вам сообщение. Существуют расширения протокола позволяющие + делать poll-модель взаимодействия, но они не везде доступны и + используются. Очень важно быть независимым от заданной модели + поведения и обмениваться данными с теми возможностями которые у вас + имеются. + +@item @url{https://ru.wikipedia.org/wiki/DTN, DTN} (сеть устойчивая к разрывам) + SMTP удалит сообщения которые не могут быть доставлены в течении + длительного времени (несколько дней). Другие решения толерантны к + длительным задержкам. + +@item Маршрутизация + UUCP и NNCP ничего не знают о маршрутизации. Вы явно должны сообщать + через какие и к каким узлам нужно посылать пакет. + +@item Поддержка телефонной сети + UUCP и FidoNet всегда из коробки поддерживали работу с модемами. + Только много лет позже они получили возможность работы поверх + TCP/IP соединений. SMTP работает только поверх TCP/IP. NNCP на + данный момент имеет только TCP демон, но ничего не мешает + использовать другой 8-бит online транспорт. + +@item Анонимные участники + NNCP и FTN являются только друг-к-другу (F2F) сетью. Это очень + безопасно и предотвращает многие возможные атаки + человека-по-середине (MitM) и + @url{https://en.wikipedia.org/wiki/Sybil_attack, Sybil}. + +@item Дружелюбность к флоппинету + Никто, кроме NNCP, не поддерживает штатный обмен данными через + переносные устройства хранения типа флеш накопителей, CD-ROM-ов, + лент и жёстких дисков. Это можно сэмулировать для большинства FTN + программного обеспечения, путём ручного копирования файлов в + входящие/исходящие директории. Но UUCP и SMTP требуют ещё больше + ручной работы для этого. + +@end table diff --git a/doc/comparison.texi b/doc/comparison.texi index c4350bdd352e159a17da073d5c7c0d7c65d2b73ffe76f179aac745cf3cf35695..6f9a154039ee658af5696af8b5c37a68c312c6233486c7d19df93d73249c802f 100644 --- a/doc/comparison.texi +++ b/doc/comparison.texi @@ -7,19 +7,20 @@ FidoNet} Technology Networks) and @url{https://en.wikipedia.org/wiki/SMTP, SMTP} (because it is also store-and-forward solution). @multitable @columnfractions 0.40 0.15 0.15 0.15 0.15 -@headitem @tab UUCP @tab FTN @tab NNCP @tab SMTP +@headitem @tab UUCP @tab FTN @tab NNCP @tab SMTP @item Ease of setup @tab Medium @tab Hard @tab Easy @tab Hard @item Mail transmission @tab @strong{Yes} @tab @strong{Yes} @tab @strong{Yes} @tab @strong{Yes} @item News transmission @tab @strong{Yes} @tab @strong{Yes} @tab No @tab No @item File transmission @tab @strong{Yes} @tab @strong{Yes} @tab @strong{Yes} @tab No +@item Chunked files @tab No @tab @strong{Yes} @tab @strong{Yes} @tab No @item Remote command execution @tab @strong{Yes} @tab No @tab No @tab No @item Resumable downloads @tab @strong{Yes} @tab @strong{Yes} @tab @strong{Yes} @tab No @item Packets prioritizing @tab @strong{Yes} @tab No @tab @strong{Yes} @tab No @item Mail compression @tab No @tab @strong{Yes} @tab @strong{Yes} @tab No @item SMTP integration @tab @strong{Yes} @tab No @tab @strong{Yes} @tab N/A @item Push/poll @tab @strong{Both} @tab @strong{Both} @tab @strong{Both} @tab Push -@item Delay tolerant @tab @strong{Yes} @tab @strong{Yes} @tab @strong{Yes} @tab No +@item DTN @tab @strong{Yes} @tab @strong{Yes} @tab @strong{Yes} @tab No @item Intended network size @tab Dozens @tab Global @tab Dozens @tab Global @item Routing @tab Manual/static @tab Federated @tab Manual/static @tab Federated @item PSTN support @tab @strong{Yes} @tab @strong{Yes} @tab Possible @tab No @@ -44,7 +45,8 @@ software comparing to Unix one. Even mail editor will be something like GoldEd, not an ordinary email client. Moreover, there is no out-of-box encryption and strong authentication involved. - NNCP requires single YAML file editing and nothing more. + NNCP requires editing of single YAML @ref{Configuration, + configuration file}. @item News transmission SMTP does not know anything about news, NNTP and so forth. Neither @@ -54,6 +56,12 @@ @item File transmission SMTP could transfer files only Base64-encoding them -- this is very inefficient. +@item Chunked files + FTN software can automatically split huge files on smaller chunks, + to reassemble it on the destination node. NNCP also supports + @ref{Chunked, that feature}, especially important when dealing with + small capacity removable storage devices. + @item Packets prioritizing UUCP and NNCP will push higher priority ("grade" in UUCP terminology) packets first. You mail will pass, even when many @@ -74,11 +82,9 @@ poll-model, but they are not used everywhere. This is very important to be independent from specified model and be able to exchange the data with possibility you have. -@item Delay tolerant +@item @url{https://en.wikipedia.org/wiki/Delay-tolerant_networking, DTN} (delay tolerant networking) SMTP will drop messages that can not be delivered for a long time - (several days). Others are - @url{https://en.wikipedia.org/wiki/Delay-tolerant_networking, - tolerant} for the long delays. + (several days). Others are tolerant for the long delays. @item Routing UUCP and NNCP does not known nothing about routing. You have to @@ -93,13 +99,14 @@ online transport. @item Anonymous peers NNCP and FTN are friend-to-friend networks exclusively. This is very - secure and mitigates many possible man-in-the-middle attacks. + secure and mitigates many possible man-in-the-middle (MitM) and + @url{https://en.wikipedia.org/wiki/Sybil_attack, Sybil} attacks. @item Sneakernet friendliness No one, except NNCP, supports data exchanging via removable storages - likes flash drives, CD-ROMs, tapes and hard drives. It can be - emulated for many FTN software, by manually copying files in its - inbound/outbound directories. But UUCP and SMTP software requires - more manual work to do so. + likes flash drives, CD-ROMs, tapes and hard drives out-of-box. It + can be emulated for many FTN software, by manually copying files in + its inbound/outbound directories. But UUCP and SMTP software + requires more manual work to do so. @end table diff --git a/doc/download.texi b/doc/download.texi index dfebc2f385c95cb1ddc7d43b11845f456bae83aa66dbddcad882ebbc1498cc11..fc342987f8273567b5d22547657a0b803650aad45091646af6949fe07a908780 100644 --- a/doc/download.texi +++ b/doc/download.texi @@ -9,11 +9,12 @@ Tarballs include all necessary required libraries: @multitable @columnfractions .50 .50 @headitem Library @tab Licence +@item @code{cypherpunks.ru/balloon} @tab GNU GPLv3+ @item @code{github.com/dustin/go-humanize} @tab MIT @item @code{github.com/flynn/noise} @tab BSD 3-Clause @item @code{github.com/go-check/check} @tab BSD 2-Clause @item @code{github.com/go-yaml/yaml} @tab Apache License 2.0 and MIT -@item @code{github.com/gorhill/cronexpr} @tab GPLv3 +@item @code{github.com/gorhill/cronexpr} @tab GNU GPLv3 @item @code{github.com/minio/blake2b-simd} @tab Apache License 2.0 @item @code{golang.org/x/crypto} @tab BSD 3-Clause @item @code{golang.org/x/net} @tab BSD 3-Clause @@ -22,6 +23,10 @@ @end multitable @multitable {XXXXX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} @headitem Version @tab Size @tab Tarball @tab SHA256 checksum + +@item @ref{Release 0.6, 0.6} @tab 746 KiB +@tab @url{download/nncp-0.6.tar.xz, link} @url{download/nncp-0.6.tar.xz.sig, sign} +@tab @code{DCFEE3F9 F669AC28 563C50DB 67BB8B43 0CFF4AB6 EC770ACE B5378D0B B40C0656} @item @ref{Release 0.5, 0.5} @tab 743 KiB @tab @url{download/nncp-0.5.tar.xz, link} @url{download/nncp-0.5.tar.xz.sig, sign} diff --git a/doc/eblob.texi b/doc/eblob.texi new file mode 100644 index 0000000000000000000000000000000000000000..54bda4d0609e1e6a0377dc9df546f9dfe68a50311399e6957835aa77f678a5da --- /dev/null +++ b/doc/eblob.texi @@ -0,0 +1,67 @@ +@node EBlob +@unnumbered EBlob format + +Eblob is an encrypted blob (binary large object, in the terms of +databases), holding any kind of symmetrically encrypted data with the +passphrase used to derive the key. It is used to secure configuration +files, holding valuable private keys, allowing them to be transferred +safely everywhere. + +In fact it uses two factors for securing the data: + +@itemize +@item @strong{salt}, that is kept inside @file{eblob}, something @emph{you have} +@item @strong{passphrase}, that is kept inside the head, something @emph{you know} +@end itemize + +Whole security depends on the passphrase itself. Pay attention that this +is @strong{not} the password. Password is a short string of high entropy +(highly random) characters, but passphrase is (very) long string of +low-entropy characters. Low-entropy text is much more easier to +remember, and its length provides pretty enough entropy as a result. + +Password strengthening function is applied to that passphrase to +mitigate brute-force and dictionary attacks on it. Here, +@url{https://crypto.stanford.edu/balloon/, Balloon} memory-hard password +hashing function is used, together with BLAKE2b-256 hash. It has proven +memory-hardness properties, very easy to implement, resistant to cache +attacks and seems more secure than Argon2 +(@url{https://password-hashing.net/, Password Hashing Competition} +winner). + +Eblob is an @url{https://tools.ietf.org/html/rfc4506, XDR}-encoded structure: + +@verbatim ++-------+------------------+------------+ +| MAGIC | S | T | P | SALT | BLOB | MAC | ++-------+------------------+------------+ +@end verbatim + +@multitable @columnfractions 0.2 0.3 0.5 +@headitem @tab XDR type @tab Value +@item Magic number @tab + 8-byte, fixed length opaque data @tab + @verb{|N N C P B 0x00 0x00 0x01|} +@item S, T, P @tab + unsigned integer @tab + Space cost, time cost and parallel jobs number +@item Salt @tab + 32 bytes, fixed length opaque data @tab + Randomly generated salt +@item Blob @tab + variable length opaque data @tab + Encrypted data itself +@item MAC @tab + 32 bytes, fixed length opaque data @tab + BLAKE2b-256 MAC of encrypted blob +@end multitable + +Blob's encryption is done using +@url{https://www.schneier.com/academic/twofish/, Twofish} algorithm with +256-bit key in +@url{https://en.wikipedia.org/wiki/Counter_mode#Counter_.28CTR.29, CTR} +mode of operation with zero initialization vector. +@code{balloon(BLAKE2b-256, S, T, P, salt, password)} gives the main key, +that is fed to @url{https://en.wikipedia.org/wiki/HKDF, +HKDF}-BLAKE2b-256 KDF. Actual encryption key for Twofish and +authentication key for MAC are derived from that KDF. diff --git a/doc/index.texi b/doc/index.texi index a99d735223e08bbf16fbac5fdaf3f3cd7b13cef9f9408816194370f50b93de7d..aa572025b6891c6cdb9d06c447d986d7abb54177899cb941743f1bd480cab1cf 100644 --- a/doc/index.texi +++ b/doc/index.texi @@ -20,39 +20,7 @@ @node Top @top NNCP -NNCP (Node to Node copy) is a collection of utilities simplifying -secure store-and-forward files and mail exchanging. - -This utilities are intended to help build up small size (dozens of -nodes) ad-hoc @url{https://en.wikipedia.org/wiki/Friend-to-friend, -friend-to-friend} (F2F) statically routed -@url{https://en.wikipedia.org/wiki/Darknet, darknet} networks for -fire-and-forget secure reliable files, file requests and Internet mail -transmission. All packets are integrity checked, -@url{https://en.wikipedia.org/wiki/End-to-end_encryption, end-to-end} -encrypted, explicitly authenticated by known participants public keys. -@url{https://en.wikipedia.org/wiki/Onion_routing, Onion encryption} is -applied to relayed packets. Each node acts both as a client and server, -can use push and poll behaviour model. - -Out-of-box offline @url{https://en.wikipedia.org/wiki/Sneakernet, -sneakernet/floppynet}, @url{https://en.wikipedia.org/wiki/Dead_drop, -dead drops} and @url{https://en.wikipedia.org/wiki/Air_gap_(networking), -air-gapped} computers support. But online TCP daemon with full-duplex -resumable data transmission exists. - -NNCP is @url{https://www.gnu.org/philosophy/pragmatic.html, copylefted} -@url{https://www.gnu.org/philosophy/free-sw.html, free software} -licenced under @url{https://www.gnu.org/licenses/gpl-3.0.html, GPLv3+}. -It should work on all @url{https://en.wikipedia.org/wiki/POSIX, -POSIX}-compatible systems. Easy integration with existing -@url{https://en.wikipedia.org/wiki/SMTP, SMTP} servers. Single -@url{http://yaml.org/, YAML} configuration file. - -Why create yet another store-and-forward solution when UUCP, FTN and -even SMTP exists? Look in @ref{Comparison, comparison} section! -Simplicity, cryptographic security, sneakernet compatibility and easy -integration with current SMTP servers are the reasons. +@include about.texi @center Interested? @ref{Tarballs, @strong{Download it}}! @@ -61,16 +29,19 @@ * Comparison:: * Use cases:: * Workflow:: * News:: +* Информация на русском: Русский. * Installation:: * Configuration:: * Call configuration: Call. * Integration with Postfix: Postfix. * Commands:: * Niceness:: +* Chunked files: Chunked. * Spool directory: Spool. * Log format: Log. * Packet format: Packet. * Sync protocol: Sync. +* EBlob format: EBlob. * Thanks:: * Contacts and feedback: Contacts. * Copying conditions: Copying. @@ -80,16 +51,19 @@ @include comparison.texi @include usecases.texi @include workflow.texi @include news.texi +@include russian.texi @include install.texi @include cfg.texi @include call.texi @include postfix.texi @include cmds.texi @include niceness.texi +@include chunked.texi @include spool.texi @include log.texi @include pkt.texi @include sp.texi +@include eblob.texi @include thanks.texi @include contacts.texi diff --git a/doc/install.texi b/doc/install.texi index e098af4878bdbfb9848b03c1b339faff285b4413cb1c8b9e03105d6a00051311..abc8255da72cccc9167f8030fa5aa69105ab4ac0e0fafb21887a8b2ed0b020f8 100644 --- a/doc/install.texi +++ b/doc/install.texi @@ -1,8 +1,7 @@ @node Installation @unnumbered Installation -NNCP should run on any POSIX-compatible operating system with file -systems supporting directory and filename's length up to 57 characters. +NNCP should run on any POSIX-compatible operating system. NNCP is written on @url{https://golang.org/, Go} programming language and you have to install Go compiler (1.7+ version is highly diff --git a/doc/news.ru.texi b/doc/news.ru.texi new file mode 100644 index 0000000000000000000000000000000000000000..73cbb411a5701729bfbdf13761583fc3e908a71f54118a55d7d62cde4c720528 --- /dev/null +++ b/doc/news.ru.texi @@ -0,0 +1,93 @@ +@node Новости +@section Новости + +@node Релиз 0.7 +@subsection Релиз 0.7 +@itemize +@item +Возможность предоставлять данные для @command{nncp-file} через +стандартный ввод, используя временный зашифрованный файл для этого. + +@item +Появилась возможность передачи файлов разбитых на части, с сопутствующей +@command{nncp-reass} командой и @option{freqchunked} опцией +конфигурационного файла. Полезно для передачи больших файлов через +маленькие устройства хранения. + +@item +@option{freqminsize} опция конфигурационного файла, аналогичная +@option{-minsize}. + +@item +Опция @option{-force} команды @command{nncp-xfer} переименована в +@option{-mkdir} для ясности. + +@item +Опция @option{-minsize} задётся в KiB, а не байтах, для удобства. + +@item +Команда @command{nncp-newcfg} переименована в @command{nncp-cfgnew}, +а @command{nncp-mincfg} в @command{nncp-cfgmin}, для того чтобы они +имели общий префикс и были сгруппированы для удобства. + +@item Появилась команда @command{nncp-cfgenc}, позволяющая +шифровать/дешифровать конфигурационный файл, чтобы безопасно его хранить +без использования OpenPGP или других подобных инструментов. + +@item +Обновлены зависимые криптографические библиотеки. +@end itemize + +@node Релиз 0.6 +@subsection Релиз 0.6 +@itemize +@item Появилась небольшая команда @command{nncp-rm}. +@item Обновлены зависимые криптографические библиотеки. +@end itemize + +@node Релиз 0.5 +@subsection Релиз 0.5 +@itemize +@item Тривиальное небольшое исправление в значениях приоритетов +по-умолчанию в @command{nncp-file} и @command{nncp-freq} командах. +@end itemize + +@node Релиз 0.4 +@subsection Релиз 0.4 +@itemize +@item Небольшое исправление в @command{nncp-call}, @command{nncp-caller}, +@command{nncp-daemon}: иногда они могли падать с segmentation fault +ошибкой (данные не терялись). +@item @command{nncp-newnode} переименована в @command{nncp-newcfg} -- +это короче и удобнее для использования. +@item Появилась команда @command{nncp-mincfg}: вспомогательная утилита +позволяющая создать минималистичный урезанный конфигурационный файл без +приватных ключей, что полезно во время использования @command{nncp-xfer}. +@end itemize + +@node Релиз 0.3 +@subsection Релиз 0.3 +Исправлена совместимость с Go 1.6. + +@node Релиз 0.2 +@subsection Релиз 0.2 +@itemize +@item @strong{Несовместимое} изменение формата пакета (магическое число +тоже изменено): поле размера пакета шифруется и не посылается в открытом +виде. +@item @option{-minsize} опция даёт возможность автоматически дополнять +исходящие пакеты до указанного минимального размера. +@item @command{nncp-daemon} и +@command{nncp-call}/@command{nncp-caller} всегда в фоне проверяют появление +исходящих @emph{tx} пакетов пока подключены. Удалённая сторона сразу же +оповещается об этом. +@item @option{-onlinedeadline} опция даёт возможность выставления +timeout-а на неактивность в online соединении, когда оно должно быть +отключено. Она может быть использована для сохранения соединения на +долгое время. +@item @option{-maxonlinetime} опция даёт возможность указания +максимального возможного времени жизни соединения. +@item Появилась @command{nncp-caller} команда: клиент TCP-демона +работающий по cron-у. +@item @command{nncp-pkt} команда может разжимать данные. +@end itemize diff --git a/doc/news.texi b/doc/news.texi index 1679b58c445b4fe3885e611885c55ae385a5c1da79e90068735168424cdf3c59..066f032441fab23dec4953ca091ec107d94633d62028b43c1e943d26694e3422 100644 --- a/doc/news.texi +++ b/doc/news.texi @@ -1,11 +1,51 @@ @node News @unnumbered News +See also this page @ref{Новости, on russian}. + +@node Release 0.7 +@section Release 0.7 +@itemize +@item +Ability to feed @command{nncp-file} from stdin, that uses an encrypted +temporary file for that. + +@item +Chunked files transmission appeared with corresponding +@command{nncp-reass} command and @option{freqchunked} configuration file +entry. Useful for transferring big files over small storage devices. + +@item +@option{freqminsize} configuration file option, analogue to +@option{-minsize} one. + +@item +@command{nncp-xfer}'s @option{-force} option is renamed to +@option{-mkdir} for clarity. + +@item +@option{-minsize} option is specified in KiBs, not bytes, for +convenience. + +@item +@command{nncp-newcfg} command is renamed to @command{nncp-cfgnew}, +and @command{nncp-mincfg} to @command{nncp-cfgmin} -- now they have +common prefix and are grouped together for convenience. + +@item +@command{nncp-cfgenc} command appeared, allowing configuration file +encryption/decryption, for keeping it safe without any either OpenPGP or +similar tools usage. + +@item +Cryptographic libraries (dependencies) are updated. +@end itemize + @node Release 0.6 @section Release 0.6 @itemize @item Small @command{nncp-rm} command appeared. -@item Cryptographic libraries (dependecies) are updated. +@item Cryptographic libraries (dependencies) are updated. @end itemize @node Release 0.5 @@ -24,7 +64,7 @@ lost). @item @command{nncp-newnode} renamed to @command{nncp-newcfg} -- it is shorter and more convenient to use. @item @command{nncp-mincfg} command appeared: helper allowing to create -minimalistic stripped down configuration files without private keys, +minimalistic stripped down configuration file without private keys, that is useful during @command{nncp-xfer} usage. @end itemize diff --git a/doc/pkt.texi b/doc/pkt.texi index 9875eb5990f7f289a2585df05c6e80a8f198979556f15981f3123bd700f3e1b9..0a2c087c70079acb92af0e53b09a5da4c07504d727f1b2e44b8b523234c14b84 100644 --- a/doc/pkt.texi +++ b/doc/pkt.texi @@ -2,8 +2,7 @@ @node Packet @unnumbered Packet format All packets are -@url{https://en.wikipedia.org/wiki/External_Data_Representation, -XDR}-encoded structures. +@url{https://tools.ietf.org/html/rfc4506, XDR}-encoded structures. @menu * Plain packet: Plain. @@ -26,7 +25,7 @@ +-------------------------------+--...---+ @end verbatim @multitable @columnfractions 0.2 0.3 0.5 -@headitem @tab XDR type @tab Value +@headitem @tab XDR type @tab Value @item Magic number @tab 8-byte, fixed length opaque data @tab @verb{|N N C P P 0x00 0x00 0x01|} @@ -81,7 +80,7 @@ +-------------------------------------+ @end verbatim @multitable @columnfractions 0.2 0.3 0.5 -@headitem @tab XDR type @tab Value +@headitem @tab XDR type @tab Value @item Magic number @tab 8-byte, fixed length opaque data @tab @verb{|N N C P E 0x00 0x00 0x01|} @@ -120,7 +119,7 @@ After the headers comes an encrypted payload size and MAC of that size. @multitable @columnfractions 0.2 0.3 0.5 -@headitem @tab XDR type @tab Value +@headitem @tab XDR type @tab Value @item Size @tab unsigned hyper integer @tab Payload size. diff --git a/doc/russian.texi b/doc/russian.texi new file mode 100644 index 0000000000000000000000000000000000000000..12fa51828bc112f6e63a1214d13dedb98742fc61dfb8dcf89b4ede6bda07397a --- /dev/null +++ b/doc/russian.texi @@ -0,0 +1,14 @@ +@node Русский +@unnumbered Информация на русском + +@menu +* Подробнее об утилитах NNCP: Об утилитах. +* Сравнение с существующими решениями: Сравнение. +* Сценарии использования:: +* Новости:: +@end menu + +@include about.ru.texi +@include comparison.ru.texi +@include usecases.ru.texi +@include news.ru.texi diff --git a/doc/sources.texi b/doc/sources.texi index ccacda1a0f3f365673edada7490a6576ccee8708a79743a687e249ebab0999b1..f2b8eb666866107b09d6e13f452e84caab85d101a0aaaf2c4ea5b7d0dcf4ab31 100644 --- a/doc/sources.texi +++ b/doc/sources.texi @@ -7,7 +7,7 @@ libraries source code. Because of that, it is recommended for porters to use @ref{Tarballs, tarballs} instead. You can obtain it by cloning @url{http://git-scm.com/, Git} -@url{http://git.cypherpunks.ru/cgit.cgi/nncp.git/log/, repository} +@url{https://git.cypherpunks.ru/cgit.cgi/nncp.git/log/, repository} and fetching dependent libraries source code as git submodules: @verbatim @@ -16,5 +16,3 @@ % cd nncp % git checkout develop % git submodule update --init @end verbatim - -Github.com mirror exists: @url{https://github.com/stargrave/nncp}. diff --git a/doc/sp.texi b/doc/sp.texi index c341c1640bdb8d0405966561fa45536bb7009abc88e9f3243b8512afa386560b..63539a7ff393aeeb4a402aa2faaed32911170069e95069cd48faf6d76e722d26 100644 --- a/doc/sp.texi +++ b/doc/sp.texi @@ -15,7 +15,7 @@ SP works on top of @url{http://noiseprotocol.org/noise.html#interactive-patterns, @code{Noise_IK_25519_ChaChaPoly_BLAKE2b}} protocol. Each Noise packet -are sent inside XDR envelope: +is sent inside an @url{https://tools.ietf.org/html/rfc4506, XDR} envelope: @verbatim +-----------------+ @@ -24,7 +24,7 @@ +-----------------+ @end verbatim @multitable @columnfractions 0.2 0.3 0.5 -@headitem @tab XDR type @tab Value +@headitem @tab XDR type @tab Value @item Magic number @tab 8-byte, fixed length opaque data @tab @verb{|N N C P S 0x00 0x00 0x01|} @@ -68,7 +68,7 @@ | INFO | NICE | SIZE | HASH | +------+--------------------+ @end verbatim @multitable @columnfractions 0.2 0.3 0.5 - @headitem @tab XDR type @tab Value + @headitem @tab XDR type @tab Value @item Niceness @tab unsigned integer @tab 1-255, file niceness level @@ -89,7 +89,7 @@ | FREQ | HASH | OFFSET | +------+---------------+ @end verbatim @multitable @columnfractions 0.2 0.3 0.5 - @headitem @tab XDR type @tab Value + @headitem @tab XDR type @tab Value @item Hash @tab 32-byte, fixed length opaque data @tab Unique file identifier, its checksum @@ -106,7 +106,7 @@ | FILE | HASH | OFFSET | PAYLOAD | +------+-------------------------+ @end verbatim @multitable @columnfractions 0.2 0.3 0.5 - @headitem @tab XDR type @tab Value + @headitem @tab XDR type @tab Value @item Hash @tab 32-byte, fixed length opaque data @tab Unique file identifier, its checksum @@ -126,7 +126,7 @@ | DONE | HASH | +------+------+ @end verbatim @multitable @columnfractions 0.2 0.3 0.5 - @headitem @tab XDR type @tab Value + @headitem @tab XDR type @tab Value @item Hash @tab 32-byte, fixed length opaque data @tab Unique file identifier, its checksum @@ -134,7 +134,7 @@ @end multitable @end table -Typical peers behaviour is following: +Typical peer's behaviour is following: @enumerate @item Perform Noise-IK handshake. @@ -163,7 +163,7 @@ @item When @emph{DONE} packet received, delete corresponding file. @item When @emph{HALT} packet received, empty file sending queue. @item @emph{FILE} sending is performed only if no other outgoing packets are queued. -@item Each second node check are there any new @emph{tx} packets +@item Each second, node checks: are there any new @emph{tx} packets appeared and queues corresponding @emph{INFO} packets. @item If no packets are sent and received during @ref{CfgOnlineDeadline, onlinedeadline} duration, then close the connection. There is no diff --git a/doc/usecases.ru.texi b/doc/usecases.ru.texi new file mode 100644 index 0000000000000000000000000000000000000000..4285c86f63e369c9239770ca3c9a852b5ce499e697118fd7ade831526ffde5f8 --- /dev/null +++ b/doc/usecases.ru.texi @@ -0,0 +1,298 @@ +@node Сценарии использования +@section Сценарии использования + +@menu +* Доступность почтового сервера время от времени: UsecaseMailRU. +* Легковесная и быстрая замена POP3/IMAP4: UsecasePOPRU. +* Ненадёжный/дорогой канал связи: UsecaseUnreliableRU. +* Медленная/дорогая связь для больших объёмов данных, плохой QoS: UsecaseQoSRU. +* Экстремальные наземные окружающие условия, нет связи: UsecaseNoLinkRU. +* Частные, изолированные MitM/Sybil-устойчивые сети: UsecaseF2FRU. +* Высоко защищённые изолированные компьютеры с воздушным зазором: UsecaseAirgapRU. +* Обход сетевой цензуры, здоровье: UsecaseCensorRU. +* Разведка, шпионаж, тайная агентура: UsecaseSpyRU. +@end menu + +@node UsecaseMailRU +@subsection Доступность почтового сервера время от времени + +Представьте, что у вас есть собственный @url{http://www.postfix.org/, +Postfix} SMTP сервер подключённый к Интернету. Но вы читаете и пишете +почтовые сообщения на своём ноутбуке, который подключается к нему лишь +время от времени. Как опустошить очередь из ожидающих сообщений когда +ноутбук подключён? + +Одна из возможностей это войти на сервер и сделать что-то типа +@command{postqueue -f}, но по-умолчанию у вас есть только несколько дней +на это, плюс отправитель будет получать уведомления о том, что его +сообщение всё ещё не доставлено. Кроме того, вы должны использовать +безопасный канал связи (SSH, VPN, итд). + +Другая возможность это использовать POP3/IMAP4 сервер, но это слишком +переусложнённо и громоздко для такой простой задачи. Не вариант. +@url{https://ru.wikipedia.org/wiki/KISS_(%D0%BF%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF), +KISS}! + +Просто скажите вашим обоим Postfix-ам (на сервере и ноутбуке) отправлять +сообщения через NNCP (@ref{nncp-mail}) на заданный узел. Это делается +аналогично тому как с UUCP, и описано в +@url{http://www.postfix.org/UUCP_README.html, документации Postfix}. + +Читайте @ref{Postfix, здесь} для более подробной информации. Вся почта +будет сохранятся в NNCP @ref{Spool, спуле}, который после обмена данных +и распаковки вызовет локальный @command{sendmail} для доставки почты, +как-будто это произошло на этой же машине. + +@node UsecasePOPRU +@subsection Легковесная и быстрая замена POP3/IMAP4 + +@ref{nncp-daemon} может быть соединён с @ref{nncp-caller} длительное +время -- он создаёт TCP соединение на многие часы. Когда SMTP сервер +получает письмо, то вызывает @ref{nncp-mail} для создания исходящего +зашифрованного пакета. Демон ежесекундно проверяет исходящую директорию +и сразу же посылает оповещение о недоставленных пакетах противоположной +стороне, которая сразу же их может скачать. + +Всего несколько дюжин байт оповещают о входящих пакетах, дюжины байт +начинающие доставку этих пакетов. Почтовые пакеты сжимаются (POP3 и +IMAP4, как правило, нет). У вас легковесный, сжатый, надёжный канал +связи с низкими задержками для почты, с сильным шифрованием и +двусторонней аутентификацией! + +@node UsecaseUnreliableRU +@subsection Ненадёжный/дорогой канал связи + +Представьте, что у вас медленный модем/радио/спутниковый канал связи, +который часто обрывается и вызывает timeout у TCP. Не все HTTP серверы +поддерживают возобновляемые скачивания. SMTP вообще не поддерживает +продолжение оборванного приёма и тяжёлые сообщения становится очень +проблематично получить. Более того, каждый обрыв может приводить к +отсылке данных с самого начала, что не всегда по карману. + +Просто отправьте вашу @ref{nncp-mail, почту} и @ref{nncp-file, файлы} +через NNCP. Вы сможете использовать или offline методы доставки -- +читайте о них в следующем разделе, либо использовать поставляемый NNCP +@ref{nncp-daemon, TCP демон}. + +Команды: + +@verbatim +% nncp-file file_i_want_to_send bob: +% nncp-file another_file bob:movie.avi +@end verbatim + +добавят в очередь отправки два файла для узла @emph{bob}. +Выстрелил-и-забыл! Теперь это работа демона (или offline передачи) +доставить частями эти файлы до удалённой системы когда она будет +доступна. + +@node UsecaseQoSRU +@subsection Медленная/дорогая связь для больших объёмов данных, плохой QoS + +Представьте, что относительно дешёвый 2 TiB переносной жёсткий диск вы +отдаёте кому-нибудь утром каждый день (и забираете назад вечером). Это +равносильно 185 мегабитному качественному однонаправленному каналу +связи. Как насчёт большего количества и бОльших жёстких дисков? Этот +метод обмена данными называется +@url{https://ru.wikipedia.org/wiki/%D0%A4%D0%BB%D0%BE%D0%BF%D0%BF%D0%B8%D0%BD%D0%B5%D1%82, +флоппинет}. + +NNCP поддерживает @ref{Niceness, приоритезацию трафика}: каждый пакет +имеет уровень "приятности", который гарантирует что он будет обработан +раньше или позднее остальных. Почти все команды имеют соответствующую +опцию: + +@verbatim +% nncp-file -nice 32 myfile node:dst +% nncp-xfer -nice 192 /mnt/shared +% nncp-call -nice 224 bob +[...] +@end verbatim + +Огромные файлы могут быть разбиты на маленькие @ref{Chunked, части}, +давая возможность передачи, по сути, любых объёмов используя накопители +небольших размеров. + +@node UsecaseNoLinkRU +@subsection Экстремальные наземные окружающие условия, нет связи + +Это, в некотором роде, вариант очень медленного канала связи. Offline +методы доставки -- единственный выбор. Просто отправьте, файлы как было +показано в предыдущем разделе, но используйте переносные накопители для +передачи пакетов другим узлам. + +Представьте, что вы послали два файла узлу @emph{bob}. Вставьте USB +устройство хранения, подмонтируйте и запустите @ref{nncp-xfer}: + +@verbatim +% nncp-xfer -node bob /media/usbstick +@end verbatim + +чтобы скопировать все исходящие пакеты относящиеся к @emph{bob}. +Используйте @option{-mkdir} опцию чтобы создать все необходимые +директории на накопителе, если их нет (например когда запускаемся первый +раз). + +Если вы используете один и тот же накопитель для передачи данных и к +@emph{bob} и к @emph{alice}, то тогда просто не указывайте +@option{-node} опцию, чтобы скопировать все доступные исходящие пакеты. + +@verbatim +% nncp-xfer /media/usbstick +@end verbatim + +Размонтируйте и передайте накопитель Бобу и Алисе. Когда они вставят +накопитель в свои компьютеры, то выполнят точно такую же команду: + +@verbatim +% nncp-xfer /media/usbstick +@end verbatim + +чтобы найти все пакеты относящиеся к их узлу и локально скопируют для +дальнейшей обработки. @command{nncp-xfer} это единственная команда +используемая с переносными устройствами хранения. + +@node UsecaseF2FRU +@subsection Частные, изолированные MitM/Sybil-устойчивые сети + +Все Интернет соединения могут быть прослушаны и сфальсифицированы. Вы +@strong{вынуждены} использовать шифрование и аутентификацию для +безопасности. Но очень сложно обезопасить метаданные, которые утекают +при каждой online сессии. Когда вы запускаете свой новый сверкающий +программный сервер, то имейте в виду, что может существовать огромное +количество поддельных узлов пытающихся произвести +@url{https://en.wikipedia.org/wiki/Sybil_attack, Sybil атаку}. Открытые +узел-к-узлу (peer-to-peer) сети опасны. + +Наиболее популярный криптографический протокол в Интернете это +@url{https://ru.wikipedia.org/wiki/TLS, TLS}, который крайне сложно +правильно реализовать и сконфигурировать для двусторонней аутентификации +собеседников. Не все конфигурации TLS обладают свойством +@url{https://ru.wikipedia.org/wiki/Perfect_forward_secrecy, совершенной +прямой секретности} -- все ранее перехваченные пакеты могут быть +прочтены если приватные ключи скомпрометированы. + +Друг-к-другу (friend-to-friend) сети, "тёмные сети" (darknet) могут +нивелировать возможные риски связанные с поддельными и фиктивными +узлами. Хотя они и сложнее в поддержке и требуют больше затрат на +построение. + +@ref{nncp-daemon, TCP демон} NNCP использует +@url{http://noiseprotocol.org/, Noise-IK} протокол для двусторонней +аутентификации узлов и предоставляет эффективный (оба участника могут +отослать полезную нагрузку сразу же в самом первом пакете) безопасный +транспорт с свойством совершенной прямой секретности. + +@verbatim +% nncp-daemon -bind [::]:5400 +@end verbatim +запустит TCP демон, который будет слушать входящие соединения на всех +интерфейсах. + +@verbatim +% nncp-call bob +@end verbatim +попытается подключиться к известному TCP-адресу узла @emph{bob} (взятого +из конфигурационного файла), послать все связанные с ним исходящие +пакеты и получить от него. Все прерванные передачи будут автоматически +возобновлены. + +@node UsecaseAirgapRU +@subsection Высокозащищённые изолированные компьютеры с воздушным зазором + +Если вы сильно беспокоитесь о безопасности, то компьютер с +@url{https://ru.wikipedia.org/wiki/%D0%92%D0%BE%D0%B7%D0%B4%D1%83%D1%88%D0%BD%D1%8B%D0%B9_%D0%B7%D0%B0%D0%B7%D0%BE%D1%80_(%D1%81%D0%B5%D1%82%D0%B8_%D0%BF%D0%B5%D1%80%D0%B5%D0%B4%D0%B0%D1%87%D0%B8_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85), +воздушным зазором} может будет вашим единственным позволительным +выбором. Компьютер без каких-либо модемов, проводных и беспроводных +сетей. Очевидно, что единственная возможность обмениваться почтой и +файлами -- использовать физически переносимые устройства хранения типа +CD-ROM, жёстких дисков, лент и USB накопителей (худший вариант, из-за +сложности подобных устройств). + +Предполагаем что у вас есть ещё один собственный узел, стоящий "до" +безопасного, который делает базовые проверки полученных накопителей, +возможно перезаписывая данные с USB/жёстких дисков на CD-RW. + +NNCP из коробки поддерживает ретрансляцию пакетов. + +@verbatim +neigh: + bob: + [...] + addrs: + lan: [fe80::5400%igb0]:5400 + bob-airgap: + [...] + via: [bob] +@end verbatim + +Такой @ref{Configuration, конфигурационный файл} говорит что у нас есть +два известных соседа: @emph{bob} и @emph{bob-airgap}. @emph{bob} +доступен через online соединение, используя @emph{lan} адрес. +@emph{bob-airgap} доступен путём посылки промежуточного ретранслируемого +пакета через узел @emph{bob}. + +Любая команда типа @command{nncp-file myfile bob-airgap:} автоматически +создаст инкапсулированный пакет: один непосредственно для целевой точки, +а другой несущий его для промежуточного узла. + +Имейте в виду, что узел-ретранслятор ничего не знает о внутреннем +пакете, кроме его полного размера и приоритета. Все промежуточные пакеты +тоже зашифрованы: используя хорошо известную технологию +@url{https://ru.wikipedia.org/wiki/%D0%9B%D1%83%D0%BA%D0%BE%D0%B2%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F, +луковой маршрутизации}. @emph{bob} не может прочитать пакеты +@emph{bob-airgap}. + +@node UsecaseCensorRU +@subsection Обход сетевой цензуры, здоровье + +Это тоже подвид плохого канала связи. Некоторые правительства склонны к +запрету @strong{любого} вида личного (приватного) общения между людьми, +разрешая только доставку развлекательного контента и доступ к популярным +социальным сетям (которые уже вовсю наводнены рекламой, локально +исполняемым @url{https://www.gnu.org/philosophy/free-sw.ru.html, +проприетарным} JavaScript кодом (для слежкой за действиями пользователя, +сбором данных), бесстыдно и бессовестно эксплуатируя базовые потребности +человека в общении). + +Это их естественное желание. Но никто вас не заставляет насильно +подчиняться огромным корпорациям типа Apple, Google или Microsoft. Ваш +выбор это создавать изолированные друг-к-другу сети с кучами безобидного +контента и приватными сообщениями. Только хищники тихо наблюдают за +своими жертвами в мире млекопитающих -- слежка и чувство что вы жертва, +сделавшая что-то плохое, вредит вашему здоровью. + +@node UsecaseSpyRU +@subsection Разведка, шпионаж, тайная агентура + +Эти ребята знают насколько небезопасен Интернет, несовместим с +понятием приватности. Им необходим быстрый сброс и забор данных. Нет +возможности провести несколько итераций туда-обратно (round trip) -- +только сбросить данные, выстрелить и забыть. Опять же, это может быть +переносной накопитель и/или +@url{https://en.wikipedia.org/wiki/USB_dead_drop, USB тайник} (dead drop), +@url{https://en.wikipedia.org/wiki/PirateBox, PirateBox}ы, +@url{https://en.wikipedia.org/wiki/Short-range_agent_communications, +связь малой дальности (SRAC)}. Короткоживущие сети малой дальности типа +Bluetooth и WiFi могут быть и довольно быстрыми, позволяя быстро +"выстреливать" порциями исходящих пакетов. + +Очень важное свойство -- компрометация этих тайников или накопителей не +должна быть ни фатальна, ни даже опасна. Пакеты посылаемые через сети +или обмениваемые через устройства -- @ref{Encrypted, зашифрованы} по +принципу точка-точка (но, к сожалению, без совершенной прямой +секретности). Никаких имён файлов, получателей почтовых сообщений не +видно. + +Общение узлов между собой происходит в, так называемой, @ref{Spool, +спул} области: директории содержащей только необработанные зашифрованные +пакеты. После передачи пакета вы всё-равно не сможете его прочитать: +необходимо запустить другую фазу: @ref{nncp-toss, распаковку}, которая +использует ваши приватные криптографические ключи. То есть, даже если вы +потеряете свой компьютер, устройства хранения и тому прочее -- это не +так плохо, потому-что вы не носите с собой приватные ключи (ведь так?), +вы не "распаковываете" эти пакеты сразу же на том же самом устройстве. +Распаковка (чтение этих зашифрованных пакетов с извлечением переданных +файлов и почтовых сообщений) может и должна бы быть произведена на +отдельном компьютере (@ref{nncp-cfgmin} команда может помочь с созданием +конфигурационного файла без приватных ключей для этой цели). diff --git a/doc/usecases.texi b/doc/usecases.texi index 38b23b03548c1eda2eb42283c9bf5c3cb6f02f649337ebd6327cbf7d18e58d0f..6e19e8e47e4b7fc0e722e1bb3e8a29ff507e2bdeb5cb099add0737dd03137e36 100644 --- a/doc/usecases.texi +++ b/doc/usecases.texi @@ -7,9 +7,9 @@ * Lightweight fast POP3/IMAP4 replacement: UsecasePOP. * Unreliable/expensive communication link: UsecaseUnreliable. * Slow/expensive link for high-volume data, bad QoS: UsecaseQoS. * Extreme terrestrial environments, no link: UsecaseNoLink. -* Private, isolated MitM-resistant networks: UsecaseF2F. +* Private, isolated MitM/Sybil-resistant networks: UsecaseF2F. * Highly secure isolated air-gap computers: UsecaseAirgap. -* Network censorship bypassing: UsecaseCensor. +* Network censorship bypassing, health: UsecaseCensor. * Reconnaissance, spying, intelligence, covert agents: UsecaseSpy. @end menu @@ -27,7 +27,8 @@ receive notification emails that his messages still are not delivered yet. Also you must have secure link (SSH, VPN, etc). Another possibility is to use POP3/IMAP4 servers, but this is too -overcomplicated and bloated for the simple task. Not an option. KISS! +overcomplicated and bloated for the simple task. Not an option. +@url{https://en.wikipedia.org/wiki/KISS_principle, KISS}! Just tell both of your Postfixes (on the server and notebook) to drop email as a mail via NNCP (@ref{nncp-mail}) to specified node. This is @@ -76,9 +77,9 @@ % nncp-file file_i_want_to_send bob: % nncp-file another_file bob:movie.avi @end verbatim -will queue two files for sending to @code{emph} node. Fire and forget! -Now this is daemon's job (or offline transfer) to send this file part by -part to remote system when it is available. +will queue two files for sending to @emph{bob} node. Fire and forget! +Now this is daemon's job (or offline transfer) to send this files part +by part to remote system when it is available. @node UsecaseQoS @section Slow/expensive link for high-volume data, bad QoS @@ -101,6 +102,10 @@ % nncp-call -nice 224 bob [...] @end verbatim +Huge files could be split on smaller @ref{Chunked, chunks}, giving +possibility to transfer virtually any volumes using small capacity +storages. + @node UsecaseNoLink @section Extreme terrestrial environments, no link @@ -115,20 +120,19 @@ @verbatim % nncp-xfer -node bob /media/usbstick @end verbatim -to copy all outbound packets related to @emph{bob}'s node. Use -@option{-force} option to forcefully create related directory on USB -storage if they are missing (for example when running for the first -time). +to copy all outbound packets related to @emph{bob}. Use @option{-mkdir} +option to create related directory on USB storage if they are missing +(for example when running for the first time). If you use single storage device to transfer data both to @emph{bob} and -@emph{alice}, then just omit @option{-node} option to copy all existing -outgoing packets to that storage device. +@emph{alice}, then just omit @option{-node} option to copy all available +outgoing packets. @verbatim % nncp-xfer /media/usbstick @end verbatim -Unmount it and transfer somehow to Bob and Alice. When they will insert +Unmount it and transfer storage to Bob and Alice. When they will insert it in their computers, they will use exactly the same command: @verbatim @@ -136,11 +140,11 @@ % nncp-xfer /media/usbstick @end verbatim to find all packets related to their node and copy them locally for -further processing. nncp-xfer is the only command used with removable -devices. +further processing. @command{nncp-xfer} is the only command used with +removable devices. @node UsecaseF2F -@section Private, isolated MitM-resistant networks +@section Private, isolated MitM/Sybil-resistant networks All Internet connections can be eavesdropped and forged. You @strong{have to} to use encryption and authentication for securing them. @@ -152,7 +156,7 @@ peer-to-peer networking is dangerous thing to do. The most popular cryptographic protocol in Internet is @url{https://en.wikipedia.org/wiki/Transport_Layer_Security, TLS} that -is very hard to implement right and hard to configure for mutual +is very hard to implement correctly and hard to configure for mutual participants authentication. Not all TLS configurations and related protocols provide @url{https://en.wikipedia.org/wiki/Forward_secrecy, forward secrecy} property -- all previously intercepted packets could be @@ -220,18 +224,20 @@ automatically create an encapsulated packet: one for the destination endpoint, and other carrying it for intermediate relaying node. Pay attention that relaying node knows nothing about the packet inside, -but just its size and priority. Transition packets are encrypted too. -@emph{bob} can not read @emph{bob-airgap}'s packets. +but just its size and priority. Transition packets are encrypted too: +using well-known @url{https://en.wikipedia.org/wiki/Onion_routing, onion +routing} technology. @emph{bob} can not read @emph{bob-airgap}'s packets. @node UsecaseCensor -@section Network censorship bypassing +@section Network censorship bypassing, health This is some kind of bad link too. Some governments tend to forbid @strong{any} kind of private communication between people, allowing only entertainment content delivering and popular social networks access (that are already bloated with advertisements, locally executed -proprietary JavaScript code (for spying on user activities, collect data -on them), shamelessly exploiting the very basic human need of communication). +@url{https://www.gnu.org/philosophy/free-sw.html, proprietary} +JavaScript code (for spying on user activities, collect data on them), +shamelessly exploiting the very basic human need of communication). This is their natural wish. But nobody forces you to obey huge corporations like Apple, Google or Microsoft. It is your choice to @@ -246,9 +252,9 @@ Those guys know how Internet is a dangerous place incompatible with privacy. They require quick, fast dropping and picking of data. No possibility of many round-trips -- just drop the data, fire-and-forget. -It could be either removable media again, or -@url{https://en.wikipedia.org/wiki/USB_dead_drop, USB dead drops}, or -@url{https://en.wikipedia.org/wiki/PirateBox, PirateBox}es, or +It could be either removable media again and/or +@url{https://en.wikipedia.org/wiki/USB_dead_drop, USB dead drops}, +@url{https://en.wikipedia.org/wiki/PirateBox, PirateBox}es, @url{https://en.wikipedia.org/wiki/Short-range_agent_communications, SRAC}. Short lived short range networks like Bluetooth and WiFi can also be pretty fast, allowing to quickly fire chunks of queued packets. @@ -259,7 +265,7 @@ the network and exchanged via those devices are end-to-end @ref{Encrypted, encrypted} (but unfortunately lacking forward secrecy). No filenames, mail recipients are seen. -All communications are done with so-called @ref{Spool, spool} area: +All node communications are done with so-called @ref{Spool, spool} area: directory containing only those unprocessed encrypted packets. After packet transfer you still can not read any of them: you have to run another stage: @ref{nncp-toss, tossing}, that involves your private @@ -268,4 +274,5 @@ and so on -- it is not so bad, because you are not carrying private keys with it (don't you?), you do not "toss" those packets immediately on the same device. Tossing (reading those encrypted packets and extracting transferred files and mail messages) could and should be done on a -separate computer. +separate computer (@ref{nncp-cfgmin} command could help creating +configuration file without private keys for that purpose). diff --git a/doc/workflow.texi b/doc/workflow.texi index a84e7a685a7810d30f99797a5579ecf92cb66fe1d2706c5152d249518a589faf..6e6bf59d1825c34d7212d9804c507e68fcebc9a73f62762859eb105bea26debf 100644 --- a/doc/workflow.texi +++ b/doc/workflow.texi @@ -5,7 +5,7 @@ NNCP consists of several utilities. As a rule you will have the following workflow: @enumerate -@item Run @ref{nncp-newcfg} on each node to create an initial +@item Run @ref{nncp-cfgnew} on each node to create an initial @ref{Configuration, configuration} file. @item Tune it up and set at least @ref{Spool, spool} and log paths. @item Share your public keys and reachability addressees with your diff --git a/makedist.sh b/makedist.sh index 58c2e8c96b8e420ae9012aa803726a58a5d48600268d535fc726ec6d736d347d..998a12876997c80f391f1762e0d9c8f906e10310484197c1047e30296bf895e4 100755 --- a/makedist.sh +++ b/makedist.sh @@ -39,6 +39,7 @@ golang.org/x/crypto/hkdf golang.org/x/crypto/nacl golang.org/x/crypto/poly1305 golang.org/x/crypto/salsa20 +golang.org/x/crypto/ssh/terminal golang.org/x/crypto/twofish golang.org/x/net/AUTHORS golang.org/x/net/CONTRIBUTORS @@ -71,8 +72,8 @@ You can obtain releases source code prepared tarballs on @url{http://www.nncpgo.org/}. EOF make -C doc -./news_and_install.sh -rm -r doc/.well-known doc/nncp.html/.well-known news_and_install.sh +./supplementary_files.sh +rm -r doc/.well-known doc/nncp.html/.well-known supplementary_files.sh find . -name .git -type d | xargs rm -fr find . -name .gitignore -delete @@ -97,7 +98,7 @@ cd $cur cat <8 ------------------------ + +Основные усовершенствования в этом релизе: + +$(git cat-file -p $release | sed -n '6,/^.*BEGIN/p' | sed '$d') + +------------------------ >8 ------------------------ + +Домашняя страница NNCP: http://www.nncpgo.org/ +Коротко об утилитах: http://www.nncpgo.org/Ob-utilitakh.html + +Исходный код и его подпись для этой версии находятся здесь: + + http://www.nncpgo.org/download/nncp-${release}.tar.xz ($size KiB) + http://www.nncpgo.org/download/nncp-${release}.tar.xz.sig + +SHA256 хэш: $hash +Идентификатор GPG ключа: 0x2B25868E75A1A953 NNCP releases +Отпечаток: 92C2 F0AE FE73 208E 46BF F3DE 2B25 868E 75A1 A953 + +Пожалуйста, все вопросы касающиеся использования NNCP, отчёты об ошибках +и патчи отправляйте в nncp-devel почтовую рассылку: +https://lists.cypherpunks.ru/pipermail/nncp-devel/ +EOF diff --git a/news_and_install.sh b/news_and_install.sh deleted file mode 100755 index 5d4738407cf76e9b9ce505cebd02bcdfc6129b6d426e6e66586804b47e828d5d..0000000000000000000000000000000000000000 --- a/news_and_install.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -texi=`mktemp` -cat > $texi < $texi < diff --git a/ports/nncp/files/nncp.newsyslog.conf.in b/ports/nncp/files/nncp.newsyslog.conf.sample.in rename from ports/nncp/files/nncp.newsyslog.conf.in rename to ports/nncp/files/nncp.newsyslog.conf.sample.in diff --git a/ports/nncp/files/pkg-deinstall.in b/ports/nncp/files/pkg-deinstall.in index c8b833d3c5c4ac7ebf7a28ed93b3b8deb1499691f2b70a33ad4b863a73824539..285f0ff0277a3a41d945cbf1197ce5ce201f392cc3712a32bda41ea295b58dfd 100644 --- a/ports/nncp/files/pkg-deinstall.in +++ b/ports/nncp/files/pkg-deinstall.in @@ -1,13 +1,9 @@ #!/bin/sh -if [ "$2" != "POST-DEINSTALL" ]; then - exit 0 -fi +[ "$2" = "POST-DEINSTALL" ] || exit 0 if [ -e %%PREFIX%%/etc/nncp.yaml ]; then echo "%%PREFIX%%/etc/nncp.yaml with your private keys is not removed!" fi -if [ -e /var/spool/nncp ]; then - echo "/var/spool/nncp is not removed!" -fi +exec rmdir /var/spool/nncp diff --git a/ports/nncp/files/pkg-install.in b/ports/nncp/files/pkg-install.in new file mode 100644 index 0000000000000000000000000000000000000000..de68d76d3ad7b164dec50cc9477f14c5f82a87c53dcdea176690407058e52076 --- /dev/null +++ b/ports/nncp/files/pkg-install.in @@ -0,0 +1,5 @@ +#!/bin/sh + +[ "$2" = "POST-INSTALL" ] || exit 0 + +[ -e /var/spool/nncp ] || exec mkdir -p /var/spool/nncp diff --git a/src/cypherpunks.ru/nncp/cfg.go b/src/cypherpunks.ru/nncp/cfg.go index a2b6247a618b9ff7486db694ffd53742107a96554f3cc1eec222e04d0e8bae5f..5aeae975d38b3f524792d3a1217d3ffa53642370a2a36112abb494b4668857b4 100644 --- a/src/cypherpunks.ru/nncp/cfg.go +++ b/src/cypherpunks.ru/nncp/cfg.go @@ -19,12 +19,15 @@ package nncp import ( + "bytes" "errors" + "log" "os" "path" "github.com/gorhill/cronexpr" "golang.org/x/crypto/ed25519" + "golang.org/x/crypto/ssh/terminal" "gopkg.in/yaml.v2" ) @@ -40,15 +43,17 @@ DefaultLogPath string = "/var/spool/nncp/log" ) type NodeYAML struct { - Id string - ExchPub string - SignPub string - NoisePub *string `noisepub,omitempty` - Sendmail []string `sendmail,omitempty` - Incoming *string `incoming,omitempty` - Freq *string `freq,omitempty` - Via []string `via,omitempty` - Calls []CallYAML `calls,omitempty` + Id string + ExchPub string + SignPub string + NoisePub *string `noisepub,omitempty` + Sendmail []string `sendmail,omitempty` + Incoming *string `incoming,omitempty` + Freq *string `freq,omitempty` + FreqChunked *uint64 `freqchunked,omitempty` + FreqMinSize *uint64 `freqminsize,omitempty` + Via []string `via,omitempty` + Calls []CallYAML `calls,omitempty` Addrs map[string]string `addrs,omitempty` @@ -144,6 +149,17 @@ return nil, errors.New("Freq path must be absolute") } freq = &fr } + var freqChunked int64 + if yml.FreqChunked != nil { + if *yml.FreqChunked == 0 { + return nil, errors.New("freqchunked value must be greater than zero") + } + freqChunked = int64(*yml.FreqChunked) * 1024 + } + var freqMinSize int64 + if yml.FreqMinSize != nil { + freqMinSize = int64(*yml.FreqMinSize) * 1024 + } defOnlineDeadline := uint(DefaultDeadline) if yml.OnlineDeadline != nil { @@ -218,6 +234,8 @@ SignPub: ed25519.PublicKey(signPub), Sendmail: yml.Sendmail, Incoming: incoming, Freq: freq, + FreqChunked: freqChunked, + FreqMinSize: freqMinSize, Calls: calls, Addrs: yml.Addrs, OnlineDeadline: defOnlineDeadline, @@ -319,9 +337,21 @@ return string(raw) } func CfgParse(data []byte) (*Ctx, error) { + var err error + if bytes.Compare(data[:8], MagicNNCPBv1[:]) == 0 { + os.Stderr.WriteString("Passphrase:") + password, err := terminal.ReadPassword(0) + if err != nil { + log.Fatalln(err) + } + os.Stderr.WriteString("\n") + data, err = DeEBlob(data, password) + if err != nil { + return nil, err + } + } var cfgYAML CfgYAML - err := yaml.Unmarshal(data, &cfgYAML) - if err != nil { + if err = yaml.Unmarshal(data, &cfgYAML); err != nil { return nil, err } if _, exists := cfgYAML.Neigh["self"]; !exists { diff --git a/src/cypherpunks.ru/nncp/chunked.go b/src/cypherpunks.ru/nncp/chunked.go new file mode 100644 index 0000000000000000000000000000000000000000..01aa49b0d93890df4db54fd475f3f44c05698d548083ad5a5cd263b146571ba3 --- /dev/null +++ b/src/cypherpunks.ru/nncp/chunked.go @@ -0,0 +1,33 @@ +/* +NNCP -- Node to Node copy, utilities for store-and-forward data exchange +Copyright (C) 2016-2017 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package nncp + +var ( + MagicNNCPMv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'M', 0, 0, 1} + + ChunkedSuffixMeta = ".nncp.meta" + ChunkedSuffixPart = ".nncp.chunk" +) + +type ChunkedMeta struct { + Magic [8]byte + FileSize uint64 + ChunkSize uint64 + Checksums [][32]byte +} diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go new file mode 100644 index 0000000000000000000000000000000000000000..73e4a689cc0406f32a9a628aab95c98f6f875e0ec619b8534443674cbe0db218 --- /dev/null +++ b/src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go @@ -0,0 +1,126 @@ +/* +NNCP -- Node to Node copy, utilities for store-and-forward data exchange +Copyright (C) 2016-2017 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +// NNCP configuration file encrypter/decrypter. +package main + +import ( + "bytes" + "errors" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + + "cypherpunks.ru/nncp" + "github.com/davecgh/go-xdr/xdr2" + "golang.org/x/crypto/blake2b" + "golang.org/x/crypto/ssh/terminal" +) + +func usage() { + fmt.Fprintf(os.Stderr, nncp.UsageHeader()) + fmt.Fprintln(os.Stderr, "nncp-cfgenc -- encrypt/decrypt configuration file\n") + fmt.Fprintf(os.Stderr, "Usage: %s [options] cfg.yaml > cfg.yaml.eblob\n", os.Args[0]) + fmt.Fprintf(os.Stderr, " %s [options] -d cfg.yaml.eblob > cfg.yaml\n", os.Args[0]) + fmt.Fprintf(os.Stderr, " %s [options] -dump cfg.yaml.eblob\n", os.Args[0]) + fmt.Fprintln(os.Stderr, "Options:") + flag.PrintDefaults() +} + +func main() { + var ( + decrypt = flag.Bool("d", false, "Decrypt the file") + dump = flag.Bool("dump", false, "Print human-readable eblob information") + sOpt = flag.Int("s", nncp.DefaultS, "Balloon space cost, in 32 bytes chunks") + tOpt = flag.Int("t", nncp.DefaultT, "Balloon time cost, number of rounds") + pOpt = flag.Int("p", nncp.DefaultP, "Balloon number of parallel jobs") + version = flag.Bool("version", false, "Print version information") + warranty = flag.Bool("warranty", false, "Print warranty information") + ) + flag.Usage = usage + flag.Parse() + if *warranty { + fmt.Println(nncp.Warranty) + return + } + if *version { + fmt.Println(nncp.VersionGet()) + return + } + + if flag.NArg() != 1 { + usage() + os.Exit(1) + } + + data, err := ioutil.ReadFile(flag.Arg(0)) + if err != nil { + log.Fatalln("Can not read data:", err) + } + if *dump { + var eblob nncp.EBlob + if _, err := xdr.Unmarshal(bytes.NewReader(data), &eblob); err != nil { + log.Fatalln(err) + } + if eblob.Magic != nncp.MagicNNCPBv1 { + log.Fatalln(errors.New("Unknown eblob type")) + } + fmt.Println("Strengthening function: Balloon with BLAKE2b-256") + fmt.Printf("Memory space cost: %d bytes\n", eblob.SCost*blake2b.Size256) + fmt.Printf("Number of rounds: %d\n", eblob.TCost) + fmt.Printf("Number of parallel jobs: %d\n", eblob.PCost) + fmt.Printf("Blob size: %d\n", len(eblob.Blob)) + os.Exit(0) + } + if *decrypt { + os.Stderr.WriteString("Passphrase:") + password, err := terminal.ReadPassword(0) + if err != nil { + log.Fatalln(err) + } + os.Stderr.WriteString("\n") + cfgRaw, err := nncp.DeEBlob(data, password) + if err != nil { + log.Fatalln(err) + } + os.Stdout.Write(cfgRaw) + } else { + os.Stderr.WriteString("Passphrase:") + password1, err := terminal.ReadPassword(0) + if err != nil { + log.Fatalln(err) + } + os.Stderr.WriteString("\n") + os.Stderr.WriteString("Repeat passphrase:") + password2, err := terminal.ReadPassword(0) + if err != nil { + log.Fatalln(err) + } + os.Stderr.WriteString("\n") + if bytes.Compare(password1, password2) != 0 { + log.Fatalln(errors.New("Passphrases do not match")) + } + eblob, err := nncp.NewEBlob(*sOpt, *tOpt, *pOpt, password1, data) + if err != nil { + log.Fatalln(err) + } + os.Stdout.Write(eblob) + } +} diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go index f4a5cc09c5bafb20268e0622a406069591b0824cf41f3a7eb0fd8035be12c134..c8e09ee1e3af85a87913383849a00f07bac292af6bf9608ad11ace0f10cca327 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go @@ -35,17 +35,21 @@ fmt.Fprintf(os.Stderr, nncp.UsageHeader()) fmt.Fprintln(os.Stderr, "nncp-file -- send file\n") fmt.Fprintf(os.Stderr, "Usage: %s [options] SRC NODE:[DST]\nOptions:\n", os.Args[0]) flag.PrintDefaults() + fmt.Fprint(os.Stderr, ` +If SRC equals to -, then read data from stdin to temporary file. +`) } func main() { var ( - cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") - niceRaw = flag.Int("nice", nncp.DefaultNiceFile, "Outbound packet niceness") - minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size") - quiet = flag.Bool("quiet", false, "Print only errors") - debug = flag.Bool("debug", false, "Print debug messages") - version = flag.Bool("version", false, "Print version information") - warranty = flag.Bool("warranty", false, "Print warranty information") + cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") + niceRaw = flag.Int("nice", nncp.DefaultNiceFile, "Outbound packet niceness") + minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size, in KiB") + chunkSize = flag.Uint64("chunked", 0, "Split file on specified size chunks, in KiB") + quiet = flag.Bool("quiet", false, "Print only errors") + debug = flag.Bool("debug", false, "Print debug messages") + version = flag.Bool("version", false, "Print version information") + warranty = flag.Bool("warranty", false, "Print warranty information") ) flag.Usage = usage flag.Parse() @@ -90,7 +94,19 @@ if err != nil { log.Fatalln("Invalid NODE specified:", err) } - if err = ctx.TxFile(node, nice, flag.Arg(0), splitted[1], int64(*minSize)); err != nil { + if *chunkSize == 0 { + err = ctx.TxFile(node, nice, flag.Arg(0), splitted[1], int64(*minSize)) + } else { + err = ctx.TxFileChunked( + node, + nice, + flag.Arg(0), + splitted[1], + int64(*minSize)*1024, + int64(*chunkSize)*1024, + ) + } + if err != nil { log.Fatalln(err) } } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go index da1865cd9946601ce1b6ffd54ae360616c020e82e1eb1c4a58a602bde4c0d845..33f582980f6242204a8cbafa793a3d3d9a97de3de2162c25ef77f661e38e2dca 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go @@ -41,7 +41,7 @@ func main() { var ( cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") niceRaw = flag.Int("nice", nncp.DefaultNiceFreq, "Outbound packet niceness") - minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size") + minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size, in KiB") quiet = flag.Bool("quiet", false, "Print only errors") debug = flag.Bool("debug", false, "Print debug messages") version = flag.Bool("version", false, "Print version information") @@ -90,7 +90,7 @@ if err != nil { log.Fatalln("Invalid NODE specified:", err) } - if err = ctx.TxFreq(node, nice, splitted[1], flag.Arg(1), int64(*minSize)); err != nil { + if err = ctx.TxFreq(node, nice, splitted[1], flag.Arg(1), int64(*minSize)*1024); err != nil { log.Fatalln(err) } } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go index 3afe652bbc5fb77b59d898f122fb6857f45bfb8b0685f8fe10bd505ac3c79c2b..db3e72c9024a56012fb2dda8b15869a056a020245221f22f4cc3ce8ed46b8fa9 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go @@ -42,7 +42,7 @@ func main() { var ( cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") niceRaw = flag.Int("nice", nncp.DefaultNiceMail, "Outbound packet niceness") - minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size") + minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size, in KiB") quiet = flag.Bool("quiet", false, "Print only errors") debug = flag.Bool("debug", false, "Print debug messages") version = flag.Bool("version", false, "Print version information") @@ -91,7 +91,7 @@ if err != nil { log.Fatalln("Can not read mail body from stdin:", err) } - if err = ctx.TxMail(node, nice, strings.Join(flag.Args()[1:], " "), body, int64(*minSize)); err != nil { + if err = ctx.TxMail(node, nice, strings.Join(flag.Args()[1:], " "), body, int64(*minSize)*1024); err != nil { log.Fatalln(err) } } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-mincfg/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go rename from src/cypherpunks.ru/nncp/cmd/nncp-mincfg/main.go rename to src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go index 5d5154dd58a3893e91ac7d350dcf34a5802a01802bdf733abf31277b15cf4e8f..008e21176d6735565c33faddf5b3bcb99af91995ea61754b4ff64d51f3bc066f 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-mincfg/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go @@ -32,7 +32,7 @@ ) func usage() { fmt.Fprintf(os.Stderr, nncp.UsageHeader()) - fmt.Fprintln(os.Stderr, "nncp-mincfg -- print stripped configuration\n") + fmt.Fprintln(os.Stderr, "nncp-cfgmin -- print stripped configuration\n") fmt.Fprintf(os.Stderr, "Usage: %s [options]\nOptions:\n", os.Args[0]) flag.PrintDefaults() } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-newcfg/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go rename from src/cypherpunks.ru/nncp/cmd/nncp-newcfg/main.go rename to src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go index 5c9fdd64e86f28c157a042cdb46599f7beb20312974d471184874048a0178ea3..423d69e5f654b6a8249af2ee09b6a720e041affb97f02116047ca1109b8af254 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-newcfg/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go @@ -30,7 +30,7 @@ ) func usage() { fmt.Fprintf(os.Stderr, nncp.UsageHeader()) - fmt.Fprintln(os.Stderr, "nncp-newcfg -- generate new configuration and keys\nOptions:") + fmt.Fprintln(os.Stderr, "nncp-cfgnew -- generate new configuration and keys\nOptions:") flag.PrintDefaults() } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go new file mode 100644 index 0000000000000000000000000000000000000000..7e0707fc93e8d38b7b4461bd5f02bdd151b521e84ea967fff802252655380cb9 --- /dev/null +++ b/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go @@ -0,0 +1,355 @@ +/* +NNCP -- Node to Node copy, utilities for store-and-forward data exchange +Copyright (C) 2016-2017 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +// Send file via NNCP +package main + +import ( + "bufio" + "bytes" + "encoding/hex" + "flag" + "fmt" + "hash" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "strconv" + "strings" + + "cypherpunks.ru/nncp" + "github.com/davecgh/go-xdr/xdr2" + "github.com/dustin/go-humanize" + "golang.org/x/crypto/blake2b" +) + +func usage() { + fmt.Fprintf(os.Stderr, nncp.UsageHeader()) + fmt.Fprintln(os.Stderr, "nncp-reass -- reassemble chunked files\n") + fmt.Fprintf(os.Stderr, "Usage: %s [options] [FILE.nncp.meta]\nOptions:\n", os.Args[0]) + flag.PrintDefaults() + fmt.Fprint(os.Stderr, ` +Neither FILE, nor -node nor -all can be set simultaneously, +but at least one of them must be specified. +`) +} + +func process(ctx *nncp.Ctx, path string, keep, dryRun, stdout, dumpMeta bool) bool { + fd, err := os.Open(path) + defer fd.Close() + if err != nil { + log.Fatalln("Can not open file:", err) + } + var metaPkt nncp.ChunkedMeta + if _, err = xdr.Unmarshal(fd, &metaPkt); err != nil { + ctx.LogE("nncp-reass", nncp.SDS{"path": path, "err": err}, "bad meta file") + return false + } + fd.Close() + if metaPkt.Magic != nncp.MagicNNCPMv1 { + ctx.LogE("nncp-reass", nncp.SDS{"path": path, "err": nncp.BadMagic}, "") + return false + } + + metaName := filepath.Base(path) + if !strings.HasSuffix(metaName, nncp.ChunkedSuffixMeta) { + ctx.LogE("nncp-reass", nncp.SDS{ + "path": path, + "err": "invalid filename suffix", + }, "") + return false + } + mainName := strings.TrimSuffix(metaName, nncp.ChunkedSuffixMeta) + if dumpMeta { + fmt.Printf("Original filename: %s\n", mainName) + fmt.Printf( + "File size: %s (%d bytes)\n", + humanize.IBytes(metaPkt.FileSize), + metaPkt.FileSize, + ) + fmt.Printf( + "Chunk size: %s (%d bytes)\n", + humanize.IBytes(metaPkt.ChunkSize), + metaPkt.ChunkSize, + ) + fmt.Printf("Number of chunks: %d\n", len(metaPkt.Checksums)) + fmt.Println("Checksums:") + for chunkNum, checksum := range metaPkt.Checksums { + fmt.Printf("\t%d: %s\n", chunkNum, hex.EncodeToString(checksum[:])) + } + return true + } + mainDir := filepath.Dir(path) + + chunksPaths := make([]string, 0, len(metaPkt.Checksums)) + for i := 0; i < len(metaPkt.Checksums); i++ { + chunksPaths = append( + chunksPaths, + filepath.Join(mainDir, mainName+nncp.ChunkedSuffixPart+strconv.Itoa(i)), + ) + } + + allChunksExist := true + for chunkNum, chunkPath := range chunksPaths { + fi, err := os.Stat(chunkPath) + if err != nil && os.IsNotExist(err) { + ctx.LogI("nncp-reass", nncp.SDS{ + "path": path, + "chunk": strconv.Itoa(chunkNum), + }, "missing") + allChunksExist = false + continue + } + var badSize bool + if chunkNum+1 == len(chunksPaths) { + badSize = uint64(fi.Size()) != metaPkt.FileSize%metaPkt.ChunkSize + } else { + badSize = uint64(fi.Size()) != metaPkt.ChunkSize + } + if badSize { + ctx.LogE("nncp-reass", nncp.SDS{ + "path": path, + "chunk": strconv.Itoa(chunkNum), + }, "invalid size") + allChunksExist = false + } + } + if !allChunksExist { + return false + } + + var hsh hash.Hash + allChecksumsGood := true + for chunkNum, chunkPath := range chunksPaths { + fd, err = os.Open(chunkPath) + if err != nil { + log.Fatalln("Can not open file:", err) + } + hsh, err = blake2b.New256(nil) + if err != nil { + log.Fatalln(err) + } + if _, err = io.Copy(hsh, bufio.NewReader(fd)); err != nil { + log.Fatalln(err) + } + fd.Close() + if bytes.Compare(hsh.Sum(nil), metaPkt.Checksums[chunkNum][:]) != 0 { + ctx.LogE("nncp-reass", nncp.SDS{ + "path": path, + "chunk": strconv.Itoa(chunkNum), + }, "checksum is bad") + allChecksumsGood = false + } + } + if !allChecksumsGood { + return false + } + if dryRun { + ctx.LogI("nncp-reass", nncp.SDS{"path": path}, "ready") + return true + } + + var dst io.Writer + var tmp *os.File + var sds nncp.SDS + if stdout { + dst = os.Stdout + sds = nncp.SDS{"path": path} + } else { + tmp, err = ioutil.TempFile(mainDir, "nncp-reass") + if err != nil { + log.Fatalln(err) + } + sds = nncp.SDS{"path": path, "tmp": tmp.Name()} + ctx.LogD("nncp-reass", sds, "created") + dst = tmp + } + dstW := bufio.NewWriter(dst) + + hasErrors := false + for chunkNum, chunkPath := range chunksPaths { + fd, err = os.Open(chunkPath) + if err != nil { + log.Fatalln("Can not open file:", err) + } + if _, err = io.Copy(dstW, bufio.NewReader(fd)); err != nil { + log.Fatalln(err) + } + fd.Close() + if !keep { + if err = os.Remove(chunkPath); err != nil { + ctx.LogE("nncp-reass", nncp.SdsAdd(sds, nncp.SDS{ + "chunk": strconv.Itoa(chunkNum), + "err": err, + }), "") + hasErrors = true + } + } + } + dstW.Flush() + if tmp != nil { + tmp.Sync() + tmp.Close() + } + ctx.LogD("nncp-reass", sds, "written") + if !keep { + if err = os.Remove(path); err != nil { + ctx.LogE("nncp-reass", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "") + hasErrors = true + } + } + if stdout { + ctx.LogI("nncp-reass", nncp.SDS{"path": path}, "done") + return !hasErrors + } + + dstPathOrig := filepath.Join(mainDir, mainName) + dstPath := dstPathOrig + dstPathCtr := 0 + for { + if _, err = os.Stat(dstPath); err != nil { + if os.IsNotExist(err) { + break + } + log.Fatalln(err) + } + dstPath = dstPathOrig + strconv.Itoa(dstPathCtr) + dstPathCtr++ + } + if err = os.Rename(tmp.Name(), dstPath); err != nil { + log.Fatalln(err) + } + ctx.LogI("nncp-reass", nncp.SDS{"path": path}, "done") + return !hasErrors +} + +func findMetas(ctx *nncp.Ctx, dirPath string) []string { + dir, err := os.Open(dirPath) + defer dir.Close() + if err != nil { + ctx.LogE("nncp-reass", nncp.SDS{"path": dirPath, "err": err}, "") + return nil + } + fis, err := dir.Readdir(0) + dir.Close() + if err != nil { + ctx.LogE("nncp-reass", nncp.SDS{"path": dirPath, "err": err}, "") + return nil + } + metaPaths := make([]string, 0) + for _, fi := range fis { + if strings.HasSuffix(fi.Name(), nncp.ChunkedSuffixMeta) { + metaPaths = append(metaPaths, filepath.Join(dirPath, fi.Name())) + } + } + return metaPaths +} + +func main() { + var ( + cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") + allNodes = flag.Bool("all", false, "Process all found chunked files for all nodes") + nodeRaw = flag.String("node", "", "Process all found chunked files for that node") + keep = flag.Bool("keep", false, "Do not remove chunks while assembling") + dryRun = flag.Bool("dryrun", false, "Do not assemble whole file") + dumpMeta = flag.Bool("dump", false, "Print decoded human-readable FILE.nncp.meta") + stdout = flag.Bool("stdout", false, "Output reassembled FILE to stdout") + quiet = flag.Bool("quiet", false, "Print only errors") + debug = flag.Bool("debug", false, "Print debug messages") + version = flag.Bool("version", false, "Print version information") + warranty = flag.Bool("warranty", false, "Print warranty information") + ) + flag.Usage = usage + flag.Parse() + if *warranty { + fmt.Println(nncp.Warranty) + return + } + if *version { + fmt.Println(nncp.VersionGet()) + return + } + + cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath)) + if err != nil { + log.Fatalln("Can not read config:", err) + } + ctx, err := nncp.CfgParse(cfgRaw) + if err != nil { + log.Fatalln("Can not parse config:", err) + } + ctx.Quiet = *quiet + ctx.Debug = *debug + + var nodeOnly *nncp.Node + if *nodeRaw != "" { + nodeOnly, err = ctx.FindNode(*nodeRaw) + if err != nil { + log.Fatalln("Invalid -node specified:", err) + } + } + + if !(*allNodes || nodeOnly != nil || flag.NArg() > 0) { + usage() + os.Exit(1) + } + if flag.NArg() > 0 && (*allNodes || nodeOnly != nil) { + usage() + os.Exit(1) + } + if *allNodes && nodeOnly != nil { + usage() + os.Exit(1) + } + + if flag.NArg() > 0 { + if !process(ctx, flag.Arg(0), *keep, *dryRun, *stdout, *dumpMeta) { + os.Exit(1) + } + return + } + + hasErrors := false + if nodeOnly == nil { + seenMetaPaths := make(map[string]struct{}) + for _, node := range ctx.Neigh { + if node.Incoming == nil { + continue + } + for _, metaPath := range findMetas(ctx, *node.Incoming) { + if _, seen := seenMetaPaths[metaPath]; seen { + continue + } + hasErrors = hasErrors || !process(ctx, metaPath, *keep, *dryRun, false, false) + seenMetaPaths[metaPath] = struct{}{} + } + } + } else { + if nodeOnly.Incoming == nil { + log.Fatalln("Specified -node does not allow incoming") + } + for _, metaPath := range findMetas(ctx, *nodeOnly.Incoming) { + hasErrors = hasErrors || !process(ctx, metaPath, *keep, *dryRun, false, false) + } + } + if hasErrors { + os.Exit(1) + } +} diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go index e59f0f06c8653bdbb74f62165153100c07c4a27d5779f7e6ee8c08cd10a80638..c4c475e6f3c4a336a7e2c67c50f5df00b1d3c94d3a74835cb0889989ae2eb03a 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go @@ -48,7 +48,7 @@ nodeRaw = flag.String("node", "", "Process only that node") niceRaw = flag.Int("nice", 255, "Minimal required niceness") rxOnly = flag.Bool("rx", false, "Only receive packets") txOnly = flag.Bool("tx", false, "Only transfer packets") - force = flag.Bool("force", false, "Force outbound directories creation") + mkdir = flag.Bool("mkdir", false, "Create necessary outbound directories") keep = flag.Bool("keep", false, "Do not delete transferred packets") quiet = flag.Bool("quiet", false, "Print only errors") debug = flag.Bool("debug", false, "Print debug messages") @@ -241,7 +241,7 @@ _, err = os.Stat(nodePath) if err != nil { if os.IsNotExist(err) { ctx.LogD("nncp-xfer", sds, "does not exist") - if !*force { + if !*mkdir { ctx.UnlockDir(dirLock) continue } @@ -316,6 +316,7 @@ ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "rename") isBad = true continue } + os.Remove(filepath.Join(dstPath, pktName+".part")) delete(sds, "tmp") ctx.LogI("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{ "size": strconv.FormatInt(copied, 10), diff --git a/src/cypherpunks.ru/nncp/eblob.go b/src/cypherpunks.ru/nncp/eblob.go new file mode 100644 index 0000000000000000000000000000000000000000..f3d4c700e5aba1c67901277e8e14a162657a9f7adf8425f362382bb78d4f5db7 --- /dev/null +++ b/src/cypherpunks.ru/nncp/eblob.go @@ -0,0 +1,153 @@ +/* +NNCP -- Node to Node copy, utilities for store-and-forward data exchange +Copyright (C) 2016-2017 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package nncp + +import ( + "bytes" + "crypto/cipher" + "crypto/rand" + "crypto/subtle" + "errors" + "io" + + "cypherpunks.ru/balloon" + "github.com/davecgh/go-xdr/xdr2" + "golang.org/x/crypto/blake2b" + "golang.org/x/crypto/hkdf" + "golang.org/x/crypto/twofish" +) + +const ( + DefaultS = 1 << 20 / 32 + DefaultT = 1 << 4 + DefaultP = 2 +) + +var ( + MagicNNCPBv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'B', 0, 0, 1} +) + +type EBlob struct { + Magic [8]byte + SCost uint32 + TCost uint32 + PCost uint32 + Salt *[32]byte + Blob []byte + MAC *[blake2b.Size256]byte +} + +// Create an encrypted blob. sCost -- memory space requirements, number +// of hash-output sized (32 bytes) blocks. tCost -- time requirements, +// number of rounds. pCost -- number of parallel jobs. +func NewEBlob(sCost, tCost, pCost int, password, data []byte) ([]byte, error) { + salt := new([32]byte) + var err error + if _, err = rand.Read(salt[:]); err != nil { + return nil, err + } + key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost) + kdf := hkdf.New(blake256, key, nil, MagicNNCPBv1[:]) + keyEnc := make([]byte, 32) + if _, err = io.ReadFull(kdf, keyEnc); err != nil { + return nil, err + } + keyAuth := make([]byte, 64) + if _, err = io.ReadFull(kdf, keyAuth); err != nil { + return nil, err + } + ciph, err := twofish.NewCipher(keyEnc) + if err != nil { + return nil, err + } + ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize)) + mac, err := blake2b.New256(keyAuth) + if err != nil { + return nil, err + } + var blob bytes.Buffer + mw := io.MultiWriter(&blob, mac) + ae := &cipher.StreamWriter{S: ctr, W: mw} + if _, err = ae.Write(data); err != nil { + return nil, err + } + macTag := new([blake2b.Size256]byte) + mac.Sum(macTag[:0]) + eblob := EBlob{ + Magic: MagicNNCPBv1, + SCost: uint32(sCost), + TCost: uint32(tCost), + PCost: uint32(pCost), + Salt: salt, + Blob: blob.Bytes(), + MAC: macTag, + } + var eblobRaw bytes.Buffer + if _, err = xdr.Marshal(&eblobRaw, &eblob); err != nil { + return nil, err + } + return eblobRaw.Bytes(), nil +} + +func DeEBlob(eblobRaw, password []byte) ([]byte, error) { + var eblob EBlob + var err error + if _, err = xdr.Unmarshal(bytes.NewReader(eblobRaw), &eblob); err != nil { + return nil, err + } + if eblob.Magic != MagicNNCPBv1 { + return nil, BadMagic + } + key := balloon.H( + blake256, + password, + eblob.Salt[:], + int(eblob.SCost), + int(eblob.TCost), + int(eblob.PCost), + ) + kdf := hkdf.New(blake256, key, nil, MagicNNCPBv1[:]) + keyEnc := make([]byte, 32) + if _, err = io.ReadFull(kdf, keyEnc); err != nil { + return nil, err + } + keyAuth := make([]byte, 64) + if _, err = io.ReadFull(kdf, keyAuth); err != nil { + return nil, err + } + ciph, err := twofish.NewCipher(keyEnc) + if err != nil { + return nil, err + } + ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize)) + mac, err := blake2b.New256(keyAuth) + if err != nil { + return nil, err + } + var blob bytes.Buffer + tr := io.TeeReader(bytes.NewReader(eblob.Blob), mac) + ae := &cipher.StreamReader{S: ctr, R: tr} + if _, err = io.Copy(&blob, ae); err != nil { + return nil, err + } + if subtle.ConstantTimeCompare(mac.Sum(nil), eblob.MAC[:]) != 1 { + return nil, errors.New("Unauthenticated blob") + } + return blob.Bytes(), nil +} diff --git a/src/cypherpunks.ru/nncp/humanizer.go b/src/cypherpunks.ru/nncp/humanizer.go index d895623e15bf73fc3bc92aecb945fe8a51018ed7f6181af21947897b8d032532..3a3ab292cd9e1a5a7339e523be4d3c2e0136eb56a5cc8306415fd376fefa100b 100644 --- a/src/cypherpunks.ru/nncp/humanizer.go +++ b/src/cypherpunks.ru/nncp/humanizer.go @@ -218,6 +218,25 @@ msg = fmt.Sprintf("Packet %s is sent", sds["hash"]) default: return s } + case "nncp-reass": + chunkNum, exists := sds["chunk"] + if exists { + msg = fmt.Sprintf( + "Reassembling chunked file \"%s\" (chunk %s): %s", + sds["path"], + chunkNum, + rem, + ) + } else { + msg = fmt.Sprintf( + "Reassembling chunked file \"%s\": %s", + sds["path"], + rem, + ) + } + if err, exists := sds["err"]; exists { + msg += ": " + err + } default: return s } diff --git a/src/cypherpunks.ru/nncp/node.go b/src/cypherpunks.ru/nncp/node.go index 39306be66a11c5dac971da5afca76dc8e635996e4fb8515e78c9516aed0ccc63..37a2645a7f9cffd267f3b8f0710b46ac165a4bb75e7df891b0bc164e2f63f3c7 100644 --- a/src/cypherpunks.ru/nncp/node.go +++ b/src/cypherpunks.ru/nncp/node.go @@ -45,6 +45,8 @@ NoisePub *[32]byte Sendmail []string Incoming *string Freq *string + FreqChunked int64 + FreqMinSize int64 Via []*NodeId Addrs map[string]string OnlineDeadline uint diff --git a/src/cypherpunks.ru/nncp/toss.go b/src/cypherpunks.ru/nncp/toss.go index a5d06b1d0501754e1614e9d9c44803736e4dad18d8660e56a1ba355553501a60..06953eb0f8a2efbdf42425700fe4b063da58412af4c9751d3361bd6d31a540d5 100644 --- a/src/cypherpunks.ru/nncp/toss.go +++ b/src/cypherpunks.ru/nncp/toss.go @@ -264,7 +264,25 @@ isBad = true goto Closing } if !dryRun { - if err = ctx.TxFile(sender, job.PktEnc.Nice, filepath.Join(*freq, src), dst, 0); err != nil { + if sender.FreqChunked == 0 { + err = ctx.TxFile( + sender, + job.PktEnc.Nice, + filepath.Join(*freq, src), + dst, + sender.FreqMinSize, + ) + } else { + err = ctx.TxFileChunked( + sender, + job.PktEnc.Nice, + filepath.Join(*freq, src), + dst, + sender.FreqMinSize, + sender.FreqChunked, + ) + } + if err != nil { ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "tx file") isBad = true goto Closing diff --git a/src/cypherpunks.ru/nncp/tx.go b/src/cypherpunks.ru/nncp/tx.go index 26a1f0dd81cc7eac370c6513408262428425b35c07dbb07d3cbcb41472b5f1e7..e50a6d1d4318fb1b5c02900c68289b72a43f4477da44c3329242d9d3e7173e56 100644 --- a/src/cypherpunks.ru/nncp/tx.go +++ b/src/cypherpunks.ru/nncp/tx.go @@ -22,14 +22,20 @@ import ( "bufio" "bytes" "compress/zlib" + "crypto/cipher" + "crypto/rand" "errors" + "hash" "io" + "io/ioutil" "os" "path/filepath" "strconv" "strings" + "github.com/davecgh/go-xdr/xdr2" "golang.org/x/crypto/blake2b" + "golang.org/x/crypto/twofish" ) func (ctx *Ctx) Tx(node *Node, pkt *Pkt, nice uint8, size, minSize int64, src io.Reader) (*Node, error) { @@ -101,8 +107,57 @@ os.Symlink(nodePath, filepath.Join(ctx.Spool, lastNode.Name)) return lastNode, err } +func prepareTxFile(srcPath string) (io.Reader, *os.File, int64, error) { + var reader io.Reader + var src *os.File + var fileSize int64 + var err error + if srcPath == "-" { + src, err = ioutil.TempFile("", "nncp-file") + if err != nil { + return nil, nil, 0, err + } + os.Remove(src.Name()) + tmpW := bufio.NewWriter(src) + + tmpKey := make([]byte, 32) + if _, err = rand.Read(tmpKey); err != nil { + return nil, nil, 0, err + } + ciph, err := twofish.NewCipher(tmpKey) + if err != nil { + return nil, nil, 0, err + } + ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize)) + encrypter := &cipher.StreamWriter{S: ctr, W: tmpW} + fileSize, err = io.Copy(encrypter, bufio.NewReader(os.Stdin)) + if err != nil { + return nil, nil, 0, err + } + tmpW.Flush() + src.Seek(0, 0) + ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize)) + reader = &cipher.StreamReader{S: ctr, R: bufio.NewReader(src)} + } else { + src, err = os.Open(srcPath) + if err != nil { + return nil, nil, 0, err + } + srcStat, err := src.Stat() + if err != nil { + return nil, nil, 0, err + } + fileSize = srcStat.Size() + reader = bufio.NewReader(src) + } + return reader, src, fileSize, nil +} + func (ctx *Ctx) TxFile(node *Node, nice uint8, srcPath, dstPath string, minSize int64) error { if dstPath == "" { + if srcPath == "-" { + return errors.New("Must provide destination filename") + } dstPath = filepath.Base(srcPath) } dstPath = filepath.Clean(dstPath) @@ -113,16 +168,14 @@ pkt, err := NewPkt(PktTypeFile, dstPath) if err != nil { return err } - src, err := os.Open(srcPath) - if err != nil { - return err + reader, src, fileSize, err := prepareTxFile(srcPath) + if src != nil { + defer src.Close() } - defer src.Close() - srcStat, err := src.Stat() if err != nil { return err } - _, err = ctx.Tx(node, pkt, nice, srcStat.Size(), minSize, bufio.NewReader(src)) + _, err = ctx.Tx(node, pkt, nice, fileSize, minSize, reader) if err == nil { ctx.LogI("tx", SDS{ "type": "file", @@ -130,7 +183,7 @@ "node": node.Id, "nice": strconv.Itoa(int(nice)), "src": srcPath, "dst": dstPath, - "size": strconv.FormatInt(srcStat.Size(), 10), + "size": strconv.FormatInt(fileSize, 10), }, "sent") } else { ctx.LogE("tx", SDS{ @@ -139,7 +192,128 @@ "node": node.Id, "nice": strconv.Itoa(int(nice)), "src": srcPath, "dst": dstPath, - "size": strconv.FormatInt(srcStat.Size(), 10), + "size": strconv.FormatInt(fileSize, 10), + "err": err, + }, "sent") + } + return err +} + +func (ctx *Ctx) TxFileChunked(node *Node, nice uint8, srcPath, dstPath string, minSize int64, chunkSize int64) error { + if dstPath == "" { + if srcPath == "-" { + return errors.New("Must provide destination filename") + } + dstPath = filepath.Base(srcPath) + } + dstPath = filepath.Clean(dstPath) + if filepath.IsAbs(dstPath) { + return errors.New("Relative destination path required") + } + reader, src, fileSize, err := prepareTxFile(srcPath) + if src != nil { + defer src.Close() + } + if err != nil { + return err + } + + leftSize := fileSize + metaPkt := ChunkedMeta{ + Magic: MagicNNCPMv1, + FileSize: uint64(fileSize), + ChunkSize: uint64(chunkSize), + Checksums: make([][32]byte, 0, (fileSize/chunkSize)+1), + } + for i := int64(0); i < (fileSize/chunkSize)+1; i++ { + hsh := new([32]byte) + metaPkt.Checksums = append(metaPkt.Checksums, *hsh) + } + var sizeToSend int64 + var hsh hash.Hash + var pkt *Pkt + var chunkNum int + var path string + for { + if leftSize <= chunkSize { + sizeToSend = leftSize + } else { + sizeToSend = chunkSize + } + path = dstPath + ChunkedSuffixPart + strconv.Itoa(chunkNum) + pkt, err = NewPkt(PktTypeFile, path) + if err != nil { + return err + } + hsh, err = blake2b.New256(nil) + if err != nil { + return err + } + _, err = ctx.Tx( + node, + pkt, + nice, + sizeToSend, + minSize, + io.TeeReader(reader, hsh), + ) + if err == nil { + ctx.LogI("tx", SDS{ + "type": "file", + "node": node.Id, + "nice": strconv.Itoa(int(nice)), + "src": srcPath, + "dst": path, + "size": strconv.FormatInt(sizeToSend, 10), + }, "sent") + } else { + ctx.LogE("tx", SDS{ + "type": "file", + "node": node.Id, + "nice": strconv.Itoa(int(nice)), + "src": srcPath, + "dst": path, + "size": strconv.FormatInt(sizeToSend, 10), + "err": err, + }, "sent") + return err + } + hsh.Sum(metaPkt.Checksums[chunkNum][:0]) + leftSize -= sizeToSend + chunkNum++ + if leftSize == 0 { + break + } + } + var metaBuf bytes.Buffer + _, err = xdr.Marshal(&metaBuf, metaPkt) + if err != nil { + return err + } + path = dstPath + ChunkedSuffixMeta + pkt, err = NewPkt(PktTypeFile, path) + if err != nil { + return err + } + metaPktSize := int64(metaBuf.Len()) + _, err = ctx.Tx(node, pkt, nice, metaPktSize, minSize, &metaBuf) + if err == nil { + ctx.LogI("tx", SDS{ + "type": "file", + "node": node.Id, + "nice": strconv.Itoa(int(nice)), + "src": srcPath, + "dst": path, + "size": strconv.FormatInt(metaPktSize, 10), + }, "sent") + } else { + ctx.LogE("tx", SDS{ + "type": "file", + "node": node.Id, + "nice": strconv.Itoa(int(nice)), + "src": srcPath, + "dst": path, + "size": strconv.FormatInt(metaPktSize, 10), "err": err, }, "sent") } diff --git a/supplementary_files.sh b/supplementary_files.sh new file mode 100755 index 0000000000000000000000000000000000000000..74b1571a502a1762fce90941b122723ef8be4ef183e7b6b27c6ccc65c8c5a916 --- /dev/null +++ b/supplementary_files.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +texi=`mktemp` + +cat > $texi < $texi < $texi < $texi <