]> Cypherpunks.ru repositories - nncp.git/commitdiff
Merge branch 'develop' 0.7
authorSergey Matveev <stargrave@stargrave.org>
Sun, 30 Apr 2017 13:56:58 +0000 (16:56 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sun, 30 Apr 2017 13:56:58 +0000 (16:56 +0300)
54 files changed:
.gitmodules
README
README.RU [new file with mode: 0644]
THANKS [deleted file]
VERSION
common.mk
doc/about.ru.texi [new file with mode: 0644]
doc/about.texi [new file with mode: 0644]
doc/call.texi
doc/cfg.texi
doc/chunked.texi [new file with mode: 0644]
doc/cmds.texi
doc/comparison.ru.texi [new file with mode: 0644]
doc/comparison.texi
doc/download.texi
doc/eblob.texi [new file with mode: 0644]
doc/index.texi
doc/install.texi
doc/news.ru.texi [new file with mode: 0644]
doc/news.texi
doc/pkt.texi
doc/russian.texi [new file with mode: 0644]
doc/sources.texi
doc/sp.texi
doc/usecases.ru.texi [new file with mode: 0644]
doc/usecases.texi
doc/workflow.texi
makedist.sh
news_and_install.sh [deleted file]
ports/nncp/Makefile
ports/nncp/files/nncp.newsyslog.conf.sample.in [moved from ports/nncp/files/nncp.newsyslog.conf.in with 100% similarity]
ports/nncp/files/pkg-deinstall.in
ports/nncp/files/pkg-install.in [new file with mode: 0644]
src/cypherpunks.ru/balloon [new submodule]
src/cypherpunks.ru/nncp/cfg.go
src/cypherpunks.ru/nncp/chunked.go [new file with mode: 0644]
src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go [new file with mode: 0644]
src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go [moved from src/cypherpunks.ru/nncp/cmd/nncp-mincfg/main.go with 97% similarity]
src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go [moved from src/cypherpunks.ru/nncp/cmd/nncp-newcfg/main.go with 97% similarity]
src/cypherpunks.ru/nncp/cmd/nncp-file/main.go
src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go
src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go
src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go [new file with mode: 0644]
src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go
src/cypherpunks.ru/nncp/eblob.go [new file with mode: 0644]
src/cypherpunks.ru/nncp/humanizer.go
src/cypherpunks.ru/nncp/node.go
src/cypherpunks.ru/nncp/toss.go
src/cypherpunks.ru/nncp/tx.go
src/github.com/dustin/go-humanize
src/golang.org/x/crypto
src/golang.org/x/net
src/golang.org/x/sys
supplementary_files.sh [new file with mode: 0755]

index ea445fbb69d73a850172f64a1ea841ff044bc8b9..ae68d8ec6d28eccfec894dc578a0059dd8175e6b 100644 (file)
@@ -30,3 +30,6 @@
 [submodule "src/github.com/gorhill/cronexpr"]
        path = src/github.com/gorhill/cronexpr
        url = https://github.com/gorhill/cronexpr.git
+[submodule "src/cypherpunks.ru/balloon"]
+       path = src/cypherpunks.ru/balloon
+       url = git://git.cypherpunks.ru/balloon.git
diff --git a/README b/README
index 5641e37d70eb2e1dfdcdf90081ff805afa07b267..be8ac6ba5faf0e6fe2b4ec18dbd5110e9de81b2d 100644 (file)
--- 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 (file)
index 0000000..466ca10
--- /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 (file)
index 939764c..0000000
--- a/THANKS
+++ /dev/null
@@ -1,5 +0,0 @@
-There are people deserving to be thanked for helping this project:
-
-* Shawn K. Quinn <skquinn at rushpost dot com> for his descriptive
-  instructions about building NNCP under Ubuntu GNU/Linux distributions
-  and bug reports
diff --git a/VERSION b/VERSION
index 5a2a5806df6e909afe3609b5706cb1012913ca0e..eb49d7c7fdcbb1b4745de39837864aa7f78570ac 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.6
+0.7
index e949d1283c2b482f89b1612a2a07352438fc068f..327f8dfff34ca607685ca0e3db9e22fe4a2765bc 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -19,15 +19,17 @@ LDFLAGS = \
 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-call:
 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,15 +70,12 @@ nncp-log:
 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 @@ install: all doc
        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 (file)
index 0000000..2478ea0
--- /dev/null
@@ -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 (file)
index 0000000..eb306ad
--- /dev/null
@@ -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.
index 509da59e213c7c1c582cb4053a8a54bb99b63358..f65a2f824e2d4850554a7fc195e05a72e41edd54 100644 (file)
@@ -8,16 +8,16 @@ Example list of call structures:
 @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
index 653c8013f3b71e4a7ee07f3af47a3faf2b0bde1b..1a37f10e5678410c3d7523ae311567a62c856e94 100644 (file)
@@ -49,6 +49,8 @@ neigh:
     signpub: GTGXG...IE3OA
     sendmail: [/usr/sbin/sendmail]
     freq: /home/bob/pub
+    freqchunked: 1024
+    freqminsize: 2048
     via: [alice]
 @end verbatim
 
@@ -99,6 +101,14 @@ omitted to forbid file uploading on that node.
 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.
 For example @verb{|[foo,bar]|} means that packet can reach current node
diff --git a/doc/chunked.texi b/doc/chunked.texi
new file mode 100644 (file)
index 0000000..1682cc3
--- /dev/null
@@ -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
index 4fa10456e5430e39b0b7b42a0434bdee076c40c5..039fc2d1a079383f45339a777e46a113a7a42661 100644 (file)
@@ -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 @@ notification.
 
 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 @@ bind to and listen.
 @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
@@ -125,6 +193,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
 file receiving.
@@ -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 @@ And with the @option{-dump} option it will give you the actual payload
 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 @@ running this command as a daemon.
 @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 @@ remove them.
 @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 (file)
index 0000000..a4d101b
--- /dev/null
@@ -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
index ed3bc48970b86403500b34e7a7d3ef5b1e13a424..e8edddd32e91383cf2fb12f20913d990f662a733 100644 (file)
@@ -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 @@ FidoNet} Technology Networks) and @url{https://en.wikipedia.org/wiki/SMTP, SMTP}
     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 @@ FidoNet} Technology Networks) and @url{https://en.wikipedia.org/wiki/SMTP, SMTP}
     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 @@ FidoNet} Technology Networks) and @url{https://en.wikipedia.org/wiki/SMTP, SMTP}
     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 @@ FidoNet} Technology Networks) and @url{https://en.wikipedia.org/wiki/SMTP, SMTP}
 
 @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
index ed8614ae79f3dca8a8ddc13977eda2f281813045..0c683d564251b2e6eb72ac63e03d55b86b74068c 100644 (file)
@@ -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
@@ -23,6 +24,10 @@ Tarballs include all necessary required libraries:
 @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}
 @tab @code{D98F9149 5A6D6726 4C659640 1AD7F400 271A58CE 5D8D4AC5 5D1CF934 59BEDFA6}
diff --git a/doc/eblob.texi b/doc/eblob.texi
new file mode 100644 (file)
index 0000000..baaa718
--- /dev/null
@@ -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.
index ceadf1cd809b607fdf49d112afa1539e7ef7d2e1..87c008e6d304d0dbcabce27404109e34cd293549 100644 (file)
@@ -20,39 +20,7 @@ A copy of the license is included in the section entitled "Copying conditions".
 @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 @@ integration with current SMTP servers are the reasons.
 * 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 @@ integration with current SMTP servers are the reasons.
 @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
 
index 9dec3824a2ecf58afb57e6ac38291d3157f8d6a9..eb8584682a04ac7b638ac8cf1c6b0e46ff395d33 100644 (file)
@@ -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 (file)
index 0000000..2f6bc2e
--- /dev/null
@@ -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
index ec43ffcd3e09c2ea4737d48efa095d3d8d55fdb7..7bd287ce0ec758d7007e3edeb687f27d1903d873 100644 (file)
@@ -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
 
index ef6bc0a5093b8bbb6a9db231a895017670bbc7f3..4549505961533aed1290bffe37d79f85824abb60 100644 (file)
@@ -2,8 +2,7 @@
 @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 @@ drive.
 @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 @@ Each encrypted packet has the following header:
 @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 @@ BLAKE2b-256} MAC is appended to the ciphertext.
 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 (file)
index 0000000..a377ea3
--- /dev/null
@@ -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
index a259df02b298880dae5bc7aba9809ecbeada2d1b..83c2b4a8265ea43e93707e71a91f0c74588d62d3 100644 (file)
@@ -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 @@ and fetching dependent libraries source code as git submodules:
 % git checkout develop
 % git submodule update --init
 @end verbatim
-
-Github.com mirror exists: @url{https://github.com/stargrave/nncp}.
index dca38c3497d5be4631bb4dc5b528f94895662a4e..919cca108c2104bc58a63f06bb70465685a114b1 100644 (file)
@@ -15,7 +15,7 @@ unacceptable performance degradation.
 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 @@ are sent inside XDR envelope:
 @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 @@ just an unsigned integer telling what body structure follows.
 +------+--------------------+
 @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 @@ just an unsigned integer telling what body structure follows.
 +------+---------------+
 @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 @@ just an unsigned integer telling what body structure follows.
 +------+-------------------------+
 @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 @@ just an unsigned integer telling what body structure follows.
 +------+------+
 @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 @@ just an unsigned integer telling what body structure follows.
 
 @end table
 
-Typical peers behaviour is following:
+Typical peer's behaviour is following:
 
 @enumerate
 @item Perform Noise-IK handshake.
@@ -163,7 +163,7 @@ delete @file{.part} suffix from file's name and send @emph{DONE} packet.
 @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 (file)
index 0000000..19885c7
--- /dev/null
@@ -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} команда может помочь с созданием
+конфигурационного файла без приватных ключей для этой цели).
index d45eeb4ddf3bac8cc038cc79fcc3ac2cd6336404..5f8fdcbb410e2b5d63b9942175fc3941a8075cda 100644 (file)
@@ -7,9 +7,9 @@
 * 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 @@ The command:
 % 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 @@ later than the other ones. Nearly all commands has corresponding option:
 [...]
 @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 @@ device, mount it and run @ref{nncp-xfer}:
 % 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 @@ it in their computers, they will use exactly the same command:
 @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 @@ feeling that you are the victim that has already done something wrong.
 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 againor
-@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).
index 5ec2c736dae95a35768990851d57d86777d3ee1b..137e7f9ec73704f30c5e13fd5004241b2e73e076 100644 (file)
@@ -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
index f6a8f0e0df0d1d7c43e823fc6fe89e35196f2561..a68f4365515873561c4e2247854ca798b836df77 100755 (executable)
@@ -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 @@ EOF
 cd $cur
 
 cat <<EOF
-Subject: NNCP $release release announcement
+Subject: [EN] NNCP $release release announcement
 
 I am pleased to announce NNCP $release release availability!
 
@@ -138,3 +139,52 @@ Fingerprint: 92C2 F0AE FE73 208E 46BF  F3DE 2B25 868E 75A1 A953
 Please send questions regarding the use of NNCP, bug reports and patches
 to mailing list: https://lists.cypherpunks.ru/pipermail/nncp-devel/
 EOF
+
+cat <<EOF
+Subject: [RU] Состоялся релиз NNCP $release
+
+Я рад сообщить о выходе релиза NNCP $release!
+
+NNCP (Node to Node copy) это набор утилит упрощающий безопасный обмен
+файлами и почтой в режиме сохранить-и-переслать.
+
+Эти утилиты предназначены помочь с построением одноранговых сетей
+небольшого размера (дюжины узлов), в режиме друг-к-другу (F2F) со
+статической маршрутизацией для безопасной надёжной передачи файлов,
+запросов на передачу файлов и Интернет почты по принципу
+выстрелил-и-забыл. Все пакеты проверяются на целостность, шифруются по
+принципу точка-точка (E2EE), аутентифицируются известными публичными
+ключами участников. Луковичное (onion) шифрование применяется ко всем
+ретранслируемым пакетам. Каждый узел выступает одновременно в роли
+клиента и сервера, может использовать как push, так и poll модель
+поведения.
+
+Поддержка из коробки offline флоппинета, тайников для сброса информации
+(dead drop) и компьютеров с "воздушным зазором" (air-gap). Но также
+существует и online TCP демон с полнодуплексной возобновляемой передачей
+данных.
+
+------------------------ >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 <releases@nncpgo.org>
+Отпечаток: 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 (executable)
index 8129fc7..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-
-texi=`mktemp`
-cat > $texi <<EOF
-\input texinfo
-@documentencoding UTF-8
-@settitle NEWS
-
-`cat doc/news.texi`
-
-@bye
-EOF
-makeinfo --plaintext -o NEWS $texi
-rm -f $texi
-
-texi=$(TMPDIR=doc mktemp)
-cat > $texi <<EOF
-\input texinfo
-@documentencoding UTF-8
-@settitle INSTALL
-
-@include install.texi
-
-@bye
-EOF
-makeinfo --plaintext -o INSTALL $texi
-rm -f $texi
index 00d8d558b92e431a9fc6c80ad13290b8ad70e09c..3738689a2fe30c54fb2f90758a944504f5520ae7 100644 (file)
@@ -1,7 +1,7 @@
 # $FreeBSD$
 
 PORTNAME=      nncp
-PORTVERSION=   0.6
+PORTVERSION=   0.7
 CATEGORIES=    net
 MASTER_SITES=  http://www.nncpgo.org/download/ \
                http://sourceforge.net/projects/nncp/files/
@@ -20,33 +20,33 @@ MAKEFILE=   BSDmakefile
 USE_RC_SUBR=   nncp-caller nncp-daemon nncp-toss
 REQUIRE=       DAEMON
 
-SUB_FILES=     pkg-message pkg-deinstall nncp.newsyslog.conf
+SUB_FILES=     pkg-message pkg-install pkg-deinstall nncp.newsyslog.conf.sample
 
-PORTDOCS=      AUTHORS NEWS README THANKS
+PORTDOCS=      AUTHORS NEWS NEWS.RU README README.RU THANKS
 INFO=          nncp
 INSTALL_TARGET=        install-strip
 
 PLIST_FILES=   bin/nncp-call \
                bin/nncp-caller \
+               bin/nncp-cfgenc \
+               bin/nncp-cfgmin \
+               bin/nncp-cfgnew \
                bin/nncp-check \
                bin/nncp-daemon \
                bin/nncp-file \
                bin/nncp-freq \
                bin/nncp-log \
                bin/nncp-mail \
-               bin/nncp-mincfg \
-               bin/nncp-newcfg \
                bin/nncp-pkt \
+               bin/nncp-reass \
                bin/nncp-rm \
                bin/nncp-stat \
                bin/nncp-toss \
                bin/nncp-xfer \
-               etc/newsyslog.conf.d/nncp.conf
-PLIST_DIRS=    /var/spool/nncp
+               "@sample etc/newsyslog.conf.d/nncp.conf.sample"
 
 post-install:
        ${MKDIR} ${STAGEDIR}${PREFIX}/etc/newsyslog.conf.d
-       ${INSTALL_DATA} ${WRKDIR}/nncp.newsyslog.conf ${STAGEDIR}${PREFIX}/etc/newsyslog.conf.d/nncp.conf
-       ${MKDIR} ${STAGEDIR}/var/spool/nncp
+       ${INSTALL_DATA} ${WRKDIR}/nncp.newsyslog.conf.sample ${STAGEDIR}${PREFIX}/etc/newsyslog.conf.d/nncp.conf.sample
 
 .include <bsd.port.mk>
index af0ca734a9e56f3145806835fadde444141580be..0c9f4f1e49d46134be819ef37aed2a3fd17e4e86 100644 (file)
@@ -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 (file)
index 0000000..ff6cd87
--- /dev/null
@@ -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/balloon b/src/cypherpunks.ru/balloon
new file mode 160000 (submodule)
index 0000000..2be0740
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 2be074075c635f95406490655039988c8e3633d8
index f89c106f3c3178dadaa3a736b8277f16ad205b7c..3400ed83a93e7d850f85da7b47653497592e14a9 100644 (file)
@@ -19,12 +19,15 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 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 @@ var (
 )
 
 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 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                }
                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 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                Sendmail:       yml.Sendmail,
                Incoming:       incoming,
                Freq:           freq,
+               FreqChunked:    freqChunked,
+               FreqMinSize:    freqMinSize,
                Calls:          calls,
                Addrs:          yml.Addrs,
                OnlineDeadline: defOnlineDeadline,
@@ -319,9 +337,21 @@ func (nodeOur *NodeOur) ToYAML() string {
 }
 
 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 (file)
index 0000000..61564c4
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+NNCP -- Node to Node copy, utilities for store-and-forward data exchange
+Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+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 (file)
index 0000000..9376d33
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+NNCP -- Node to Node copy, utilities for store-and-forward data exchange
+Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+// 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)
+       }
+}
similarity index 97%
rename from src/cypherpunks.ru/nncp/cmd/nncp-mincfg/main.go
rename to src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go
index 5b5fdf51e1085c0050c6ca5cfc991bb52f9e1770..48df34e0284f45e8a655bc7457c94ca430d7510f 100644 (file)
@@ -32,7 +32,7 @@ import (
 
 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()
 }
similarity index 97%
rename from src/cypherpunks.ru/nncp/cmd/nncp-newcfg/main.go
rename to src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go
index cd36aa9023a97fe676175513f3fed1e165498265..2601d46532fc8e0a82ebf8894568116d7d348328 100644 (file)
@@ -30,7 +30,7 @@ import (
 
 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()
 }
 
index 4fafc4834b845dd2965f47b2fb17d54216b6e79d..f3406fd455aa53cd6cb8ff9300d2b8d125ad0a0f 100644 (file)
@@ -35,17 +35,21 @@ func usage() {
        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 @@ func main() {
                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)
        }
 }
index d570bdfcc232f8fb1314fe4f4b7b1a162bee1658..a5e96e69aed5e05fdf5137ae955579cf023b5d6f 100644 (file)
@@ -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 @@ func main() {
                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)
        }
 }
index c09fbe228e2aa415bed4bb0009a24718ecc6888b..d14c3d66c077cb205d76f4f956a117e97005cd68 100644 (file)
@@ -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 @@ func main() {
                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-reass/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go
new file mode 100644 (file)
index 0000000..45354d7
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+NNCP -- Node to Node copy, utilities for store-and-forward data exchange
+Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+// 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)
+       }
+}
index b11fc5d9e328f7d920ab9f5f119c3a714c8606cc..bcd76257bf0581a0fd2abf026893014411861104 100644 (file)
@@ -48,7 +48,7 @@ func main() {
                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 @@ Tx:
                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 @@ Tx:
                                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 (file)
index 0000000..cb241ab
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+NNCP -- Node to Node copy, utilities for store-and-forward data exchange
+Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+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
+}
index 06b33bd278783573e0db04579c46d0a21d062179..49b715a1c91f6c7c118196b0bf7e948c0aac4a02 100644 (file)
@@ -218,6 +218,25 @@ func (ctx *Ctx) Humanize(s string) string {
                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
        }
index 7992f4bc8f1ce19d4c6a2b3e7d13f7cd937f1b78..22fa3fb2e03663f81f47c7ba06633d2209ea9934 100644 (file)
@@ -45,6 +45,8 @@ type Node struct {
        Sendmail       []string
        Incoming       *string
        Freq           *string
+       FreqChunked    int64
+       FreqMinSize    int64
        Via            []*NodeId
        Addrs          map[string]string
        OnlineDeadline uint
index 8a48e91d50a4d7eab80d5fa09041032763e1a9b4..4ae9711f8d7d6f42fbefb50b1363ef3001ccaef6 100644 (file)
@@ -264,7 +264,25 @@ func (ctx *Ctx) Toss(nodeId *NodeId, nice uint8, dryRun bool) bool {
                                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
index 65ad7ec3b58fbcadb1fcce340b7f2bdcf65e144d..7faef491cf0f279de5e239a7c77e9c32ee4e11d6 100644 (file)
@@ -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 @@ func (ctx *Ctx) Tx(node *Node, pkt *Pkt, nice uint8, size, minSize int64, src io
        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 @@ func (ctx *Ctx) TxFile(node *Node, nice uint8, srcPath, dstPath string, minSize
        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 @@ func (ctx *Ctx) TxFile(node *Node, nice uint8, srcPath, dstPath string, minSize
                        "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 @@ func (ctx *Ctx) TxFile(node *Node, nice uint8, srcPath, dstPath string, minSize
                        "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")
        }
index ef638b6c2e62b857442c6443dace9366a48c0ee2..259d2a102b871d17f30e3cd9881a642961a1e486 160000 (submodule)
@@ -1 +1 @@
-Subproject commit ef638b6c2e62b857442c6443dace9366a48c0ee2
+Subproject commit 259d2a102b871d17f30e3cd9881a642961a1e486
index 3cb07270c9455e8ad27956a70891c962d121a228..c7af5bf2638a1164f2eb5467c39c6cffbd13a02e 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 3cb07270c9455e8ad27956a70891c962d121a228
+Subproject commit c7af5bf2638a1164f2eb5467c39c6cffbd13a02e
index ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d..da118f7b8e5954f39d0d2130ab35d4bf0e3cb344 160000 (submodule)
@@ -1 +1 @@
-Subproject commit ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d
+Subproject commit da118f7b8e5954f39d0d2130ab35d4bf0e3cb344
index 9a7256cb28ed514b4e1e5f68959914c4c28a92e0..9ccfe848b9db8435a24c424abbc07a921adf1df5 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 9a7256cb28ed514b4e1e5f68959914c4c28a92e0
+Subproject commit 9ccfe848b9db8435a24c424abbc07a921adf1df5
diff --git a/supplementary_files.sh b/supplementary_files.sh
new file mode 100755 (executable)
index 0000000..5fce6c1
--- /dev/null
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+texi=`mktemp`
+
+cat > $texi <<EOF
+\input texinfo
+@documentencoding UTF-8
+@settitle NEWS
+
+@node News
+@unnumbered News
+
+`sed -n '5,$p' < doc/news.texi`
+
+@bye
+EOF
+makeinfo --plaintext -o NEWS $texi
+
+cat > $texi <<EOF
+\input texinfo
+@documentencoding UTF-8
+@settitle NEWS.RU
+
+@node Новости
+@unnumbered Новости
+
+`sed -n '3,$p' < doc/news.ru.texi | sed 's/^@subsection/@section/'`
+
+@bye
+EOF
+makeinfo --plaintext -o NEWS.RU $texi
+
+rm -f $texi
+
+texi=$(TMPDIR=doc mktemp)
+cat > $texi <<EOF
+\input texinfo
+@documentencoding UTF-8
+@settitle INSTALL
+
+@include install.texi
+
+@bye
+EOF
+makeinfo --plaintext -o INSTALL $texi
+rm -f $texi
+
+texi=`mktemp`
+
+cat > $texi <<EOF
+\input texinfo
+@documentencoding UTF-8
+@settitle THANKS
+
+`cat doc/thanks.texi`
+
+@bye
+EOF
+makeinfo --plaintext -o THANKS $texi
+rm -f $texi