]> Cypherpunks.ru repositories - nncp.git/commitdiff
AEAD encryption mode and new encrypted packet format
authorSergey Matveev <stargrave@stargrave.org>
Sat, 27 Apr 2019 11:09:40 +0000 (14:09 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sat, 27 Apr 2019 16:34:35 +0000 (19:34 +0300)
20 files changed:
VERSION
common.mk
doc/cmds.texi
doc/eblob.texi
doc/news.ru.texi
doc/news.texi
doc/pkt.texi
src/cypherpunks.ru/nncp/cfg.go
src/cypherpunks.ru/nncp/cmd/nncp-bundle/main.go
src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go
src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go
src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go
src/cypherpunks.ru/nncp/eblob.go
src/cypherpunks.ru/nncp/internal/.gitignore [deleted file]
src/cypherpunks.ru/nncp/internal/Makefile [deleted file]
src/cypherpunks.ru/nncp/jobs.go
src/cypherpunks.ru/nncp/pkt.go
src/cypherpunks.ru/nncp/pkt_test.go
src/cypherpunks.ru/nncp/toss.go
src/cypherpunks.ru/nncp/tx.go

diff --git a/VERSION b/VERSION
index 2f4b60750dc3500b0e4cf08f316a960a7ca42b40..5186d07068cfed4c3f4705df79c73e115dd35c43 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.4
+4.0
index 322d8d29616080708adec36183984e2bb0070d73..145e84397c3edbb4a656913382eda6dcc950d392 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -38,69 +38,65 @@ ALL = \
 
 all: $(ALL)
 
-src/cypherpunks.ru/nncp/internal/chacha20: src/golang.org/x/crypto/internal/chacha20 src/golang.org/x/crypto/internal/subtle
-       $(MAKE) -C src/cypherpunks.ru/nncp/internal
-
-nncp-bundle: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-bundle:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-bundle
 
-nncp-call: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-call:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-call
 
-nncp-caller: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-caller:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-caller
 
-nncp-cfgenc: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-cfgenc:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgenc
 
-nncp-cfgmin: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-cfgmin:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgmin
 
-nncp-cfgnew: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-cfgnew:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgnew
 
-nncp-check: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-check:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-check
 
-nncp-daemon: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-daemon:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-daemon
 
-nncp-exec: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-exec:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-exec
 
-nncp-file: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-file:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-file
 
-nncp-freq: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-freq:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-freq
 
-nncp-log: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-log:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-log
 
-nncp-pkt: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-pkt:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-pkt
 
-nncp-reass: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-reass:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-reass
 
-nncp-rm: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-rm:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-rm
 
-nncp-stat: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-stat:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-stat
 
-nncp-toss: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-toss:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-toss
 
-nncp-xfer: src/cypherpunks.ru/nncp/internal/chacha20
+nncp-xfer:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-xfer
 
-test: src/cypherpunks.ru/nncp/internal/chacha20
+test:
        GOPATH=$(GOPATH) go test -failfast cypherpunks.ru/nncp/...
 
 clean:
        rm -f $(ALL)
-       rm -fr src/cypherpunks.ru/nncp/internal/chacha20
 
 .PHONY: doc
 
index 5c954678e8cc0967291d43388d3bbd518e9e3baa..9628fa0b2f2a7a41d62afce76acc35f4c30ee076 100644 (file)
@@ -299,12 +299,13 @@ This command queues file in @ref{Spool, spool} directory immediately
 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://cr.yp.to/chacha.html,
-ChaCha20} algorithm. Data is splitted on 128 KiB blocks. Each block is
-encrypted with increasing nonce counter.
+taken from stdin, then you have to have more than 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 in AEAD mode with
+@url{https://cr.yp.to/chacha.html, ChaCha20}-@url{https://en.wikipedia.org/wiki/Poly1305, Poly1305}
+algorithms. Data is splitted on 128 KiB blocks. Each block is encrypted
+with increasing nonce counter.
 
 If @option{-chunked} is specified, then source file will be split
 @ref{Chunked, on chunks}. @option{INT} is the desired chunk size in
@@ -348,6 +349,7 @@ Parse @ref{Log, log} file and print out its records in human-readable form.
 @verbatim
 % nncp-pkt [options] < pkt
 % nncp-pkt [options] [-decompress] -dump < pkt > payload
+% nncp-pkt -overheads
 @end verbatim
 
 Low level packet parser. Normally it should not be used, but can help in
@@ -380,6 +382,8 @@ 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).
 
+@option{-overheads} options print encrypted, plain and size header overheads.
+
 @node nncp-reass
 @section nncp-reass
 
index 74e6661021ff879bbfa312684c6bde892f4d8d9f..45de886d3defe40b7a82905107f64a79293d02ca 100644 (file)
@@ -32,16 +32,16 @@ winner).
 Eblob is an @url{https://tools.ietf.org/html/rfc4506, XDR}-encoded structure:
 
 @verbatim
-+-------+------------------+------------+
-| MAGIC | S | T | P | SALT | BLOB | MAC |
-+-------+------------------+------------+
++-------+------------------+------+
+| MAGIC | S | T | P | SALT | BLOB |
++-------+------------------+------+
 @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 0x02|}
+    @verb{|N N C P B 0x00 0x00 0x03|}
 @item S, T, P @tab
     unsigned integer @tab
     Space cost, time cost and parallel jobs number
@@ -50,22 +50,19 @@ Eblob is an @url{https://tools.ietf.org/html/rfc4506, XDR}-encoded structure:
     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
+    Authenticated and Encrypted data itself
 @end multitable
 
 @enumerate
 @item generate the main key using @code{balloon(BLAKE2b-256, S, T, P,
 salt, password)}
 @item initialize @url{https://blake2.net/, BLAKE2Xb} XOF with generated
-main key and 96-byte output length
-@item feed @verb{|N N C P B 0x00 0x00 0x02|} magic number to XOF
-@item read 32-bytes of blob encryption key
-@item read 64-bytes of blob authentication key
-@item encrypt the blob using @url{https://cr.yp.to/chacha.html,
-ChaCha20}. Blob is splitted on 128 KiB blocks. Each block is encrypted
-with increasing nonce counter
-@item authenticate ciphertext with MAC
+main key and 32-byte output length
+@item feed @verb{|N N C P B 0x00 0x00 0x03|} magic number to XOF
+@item read 32-bytes of blob AEAD encryption key
+@item encrypt and authenticate blob using
+    @url{https://cr.yp.to/chacha.html, ChaCha20}-@url{https://en.wikipedia.org/wiki/Poly1305, Poly1305}.
+    Blob is splitted on 128 KiB blocks. Each block is encrypted with
+    increasing nonce counter. Eblob packet itself, with empty blob
+    field, is fed as an additional authenticated data
 @end enumerate
index 46e635732d303246423e68198c843d445a918054..53b0c2a7b7a0ea23931b2e646d59631257a007f7 100644 (file)
@@ -1,6 +1,21 @@
 @node Новости
 @section Новости
 
+@node Релиз 4.0
+@subsection Релиз 4.0
+@itemize
+@item
+@strong{Несовместимое} изменение формата зашифрованных и eblob пакетов:
+используется AEAD режим шифрования с 128 КиБ блоками, так как раньше
+@command{nncp-toss} не проверял MAC зашифрованного пакета прежде чем
+отсылать дешифрованные данные внешней команде. Старые версии не
+поддерживаются.
+@item
+Зависимые библиотеки обновлены.
+@item
+Небольшие исправления ошибок.
+@end itemize
+
 @node Релиз 3.4
 @subsection Релиз 3.4
 @itemize
index 16f1d6f3e3cfe3ab53d05051f57b1fdde00efae9..1cec8ddc84f908bda111954702a92bd918ba9bd7 100644 (file)
@@ -3,6 +3,20 @@
 
 See also this page @ref{Новости, on russian}.
 
+@node Release 4.0
+@section Release 4.0
+@itemize
+@item
+@strong{Incompatible} encrypted and eblob packet format change: AEAD
+encryption mode with 128 KiB blocks is used now, because previously
+@command{nncp-toss} did not verify encrypted packet's MAC before feeding
+decrypted data to external command. Older versions are not supported.
+@item
+Dependant libraries are updated.
+@item
+Minor bugfixes.
+@end itemize
+
 @node Release 3.4
 @section Release 3.4
 @itemize
index b1c2c38b37cb5d7ad0f2254645d6e8120b94b1e8..d216f93e96d9d05e614c30f9f39a343b833ab552 100644 (file)
@@ -28,7 +28,7 @@ drive.
 @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|}
+    @verb{|N N C P P 0x00 0x00 0x02|}
 @item Payload type @tab
     unsigned integer @tab
     0 (file), 1 (freq), 2 (exec), 3 (transition)
@@ -78,11 +78,11 @@ storages and that are synchronized between TCP daemons.
 Each encrypted packet has the following header:
 
 @verbatim
-  +------------ HEADER --------------------+   +-------- ENCRYPTED --------+
- /                                          \ /                             \
-+--------------------------------------------+------------+----...-----------+------+
-| MAGIC | NICE | SENDER | RCPT | EPUB | SIGN | SIZE | MAC | CIPHERTEXT | MAC | JUNK |
-+-------------------------------------/------\------------+----...-----------+------+
+  +------------ HEADER --------------------+   +------------- ENCRYPTED -------------+
+ /                                          \ /                                       \
++--------------------------------------------+------+---------+----------...---+------+
+| MAGIC | NICE | SENDER | RCPT | EPUB | SIGN | SIZE | BLOCK 0 | BLOCK 1  ...   | JUNK |
++-------------------------------------/------\------+---------+----------...---+------+
                                      /        \
                       +-------------------------------------+
                       | MAGIC | NICE | SENDER | RCPT | EPUB |
@@ -93,7 +93,7 @@ Each encrypted packet has the following header:
 @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 0x03|}
+    @verb{|N N C P E 0x00 0x00 0x04|}
 @item Niceness @tab
     unsigned integer @tab
     1-255, packet @ref{Niceness, niceness} level
@@ -113,12 +113,12 @@ Each encrypted packet has the following header:
 
 Signature is calculated over all previous fields.
 
-All following encryption is done using @url{https://cr.yp.to/chacha.html,
-ChaCha20} algorithm. Data is splitted on 128 KiB blocks. Each block is
-encrypted with increasing nonce counter. @url{https://blake2.net/,
-BLAKE2b-256} MAC is appended to the ciphertext.
+All following encryption is done in AEAD mode using
+@url{https://cr.yp.to/chacha.html, ChaCha20}-@url{https://en.wikipedia.org/wiki/Poly1305, Poly1305}
+algorithms. Data is splitted on 128 KiB blocks. Each block is encrypted with
+increasing nonce counter.
 
-After the headers comes an encrypted payload size and MAC of that size.
+Authenticated and encrypted size come after the header:
 
 @multitable @columnfractions 0.2 0.3 0.5
 @headitem @tab XDR type @tab Value
@@ -127,7 +127,7 @@ After the headers comes an encrypted payload size and MAC of that size.
     Payload size.
 @end multitable
 
-Next comes the actual encrypted payload with corresponding MAC.
+Then comes the actual payload.
 
 Each node has static @strong{exchange} and @strong{signature} keypairs.
 When node A want to send encrypted packet to node B, it:
@@ -143,18 +143,15 @@ When node A want to send encrypted packet to node B, it:
 @item derive the keys:
     @enumerate
     @item initialize @url{https://blake2.net/, BLAKE2Xb} XOF with
-    derived ephemeral key and 224-byte output length
-    @item feed @verb{|N N C P E 0x00 0x00 0x03|} magic number to XOF
-    @item read 32-bytes of "size" encryption key (for ChaCha20)
-    @item read 64-bytes of "size" authentication key (for BLAKE2b-MAC)
-    @item read 32-bytes of payload encryption key
-    @item read 64-bytes of payload authentication key
-    @item optionally read 32-bytes pad generation key (for ChaCha20)
+    derived ephemeral key and 96-byte output length
+    @item feed @verb{|N N C P E 0x00 0x00 0x04|} magic number to XOF
+    @item read 32-bytes of "size" AEAD encryption key
+    @item read 32-bytes of payload AEAD encryption key
+    @item optionally read 32-bytes pad generation key
     @end enumerate
-@item encrypts size, appends its ciphertext to the header
-@item appends MAC tag over that ciphertext
-@item encrypts and appends payload ciphertext
-@item appends MAC tag over that payload ciphertext
+@item encrypts size, appends its authenticated ciphertext to the header
+@item encrypts payload, appends its authenticated ciphertext
 @item possibly appends any kind of "junk" noise data to hide real
-    payload's size from the adversary
+    payload's size from the adversary (generated using XOF with
+    unlimited output length)
 @end enumerate
index 79ad5cadcc502488892e3bae94764857dfaafaee..9519f963a6d805d9a1b2eb880d2407e7b46ae38e 100644 (file)
@@ -371,7 +371,7 @@ func (nodeOur *NodeOur) ToYAML() string {
 
 func CfgParse(data []byte) (*Ctx, error) {
        var err error
-       if bytes.Compare(data[:8], MagicNNCPBv2[:]) == 0 {
+       if bytes.Compare(data[:8], MagicNNCPBv3[:]) == 0 {
                os.Stderr.WriteString("Passphrase:")
                password, err := terminal.ReadPassword(0)
                if err != nil {
index eaa61a4a8a2434386aa4bd9eda94e53702beac26..553a6d11a15afa325a0d69ba09c9933b57e968c1 100644 (file)
@@ -236,7 +236,7 @@ func main() {
                                ctx.LogD("nncp-bundle", sds, "Bad packet structure")
                                continue
                        }
-                       if pktEnc.Magic != nncp.MagicNNCPEv3 {
+                       if pktEnc.Magic != nncp.MagicNNCPEv4 {
                                ctx.LogD("nncp-bundle", sds, "Bad packet magic number")
                                continue
                        }
index 5947f67af4ea5b5ed45389e495fb42f3a10a7fe4..be009f4e81e51ed6da38db857fe642a7e61937ac 100644 (file)
@@ -79,7 +79,7 @@ func main() {
                if _, err := xdr.Unmarshal(bytes.NewReader(data), &eblob); err != nil {
                        log.Fatalln(err)
                }
-               if eblob.Magic != nncp.MagicNNCPBv2 {
+               if eblob.Magic != nncp.MagicNNCPBv3 {
                        log.Fatalln(errors.New("Unknown eblob type"))
                }
                fmt.Println("Strengthening function: Balloon with BLAKE2b-256")
index f6327419a93d845f669bdafbf54fa2e595097b11..1ccea9e7def2aa244f6f10612c54832d1b321ed4 100644 (file)
@@ -31,7 +31,6 @@ import (
 
        "cypherpunks.ru/nncp"
        "github.com/davecgh/go-xdr/xdr2"
-       "golang.org/x/crypto/blake2b"
 )
 
 func usage() {
@@ -44,6 +43,7 @@ func usage() {
 
 func main() {
        var (
+               overheads  = flag.Bool("overheads", false, "Print packet overheads")
                dump       = flag.Bool("dump", false, "Write decrypted/parsed payload to stdout")
                decompress = flag.Bool("decompress", false, "Try to zlib decompress dumped data")
                cfgPath    = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
@@ -61,8 +61,18 @@ func main() {
                return
        }
 
+       if *overheads {
+               fmt.Printf(
+                       "Plain: %d\nEncrypted: %d\nSize: %d\n",
+                       nncp.PktOverhead,
+                       nncp.PktEncOverhead,
+                       nncp.PktSizeOverhead,
+               )
+               return
+       }
+
        var err error
-       beginning := make([]byte, nncp.PktOverhead-8-2*blake2b.Size256)
+       beginning := make([]byte, nncp.PktOverhead)
        if _, err = io.ReadFull(os.Stdin, beginning); err != nil {
                log.Fatalln("Not enough data to read")
        }
@@ -121,7 +131,7 @@ func main() {
        }
        var pktEnc nncp.PktEnc
        _, err = xdr.Unmarshal(bytes.NewReader(beginning), &pktEnc)
-       if err == nil && pktEnc.Magic == nncp.MagicNNCPEv3 {
+       if err == nil && pktEnc.Magic == nncp.MagicNNCPEv4 {
                if *dump {
                        ctx, err := nncp.CtxFromCmdline(*cfgPath, "", "", false, false)
                        if err != nil {
index b8c6cc675ab1b8782cb429a12cd4076e29564897..102292f2747c8fcda59e28e5508dd29e9f39c155 100644 (file)
@@ -170,7 +170,7 @@ func main() {
                        }
                        var pktEnc nncp.PktEnc
                        _, err = xdr.Unmarshal(fd, &pktEnc)
-                       if err != nil || pktEnc.Magic != nncp.MagicNNCPEv3 {
+                       if err != nil || pktEnc.Magic != nncp.MagicNNCPEv4 {
                                ctx.LogD("nncp-xfer", sds, "is not a packet")
                                fd.Close()
                                continue
index 51f9709b23d2767ef7940355b54df8032b67de1d..816388dfbb98878655153dbe6060c934461c743c 100644 (file)
@@ -21,15 +21,12 @@ package nncp
 import (
        "bytes"
        "crypto/rand"
-       "crypto/subtle"
-       "errors"
        "hash"
-       "io"
 
        "cypherpunks.ru/balloon"
-       "cypherpunks.ru/nncp/internal/chacha20"
        "github.com/davecgh/go-xdr/xdr2"
        "golang.org/x/crypto/blake2b"
+       "golang.org/x/crypto/chacha20poly1305"
 )
 
 const (
@@ -39,7 +36,7 @@ const (
 )
 
 var (
-       MagicNNCPBv2 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'B', 0, 0, 2}
+       MagicNNCPBv3 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'B', 0, 0, 3}
 )
 
 type EBlob struct {
@@ -49,7 +46,6 @@ type EBlob struct {
        PCost uint32
        Salt  *[32]byte
        Blob  []byte
-       MAC   *[blake2b.Size256]byte
 }
 
 func blake256() hash.Hash {
@@ -69,46 +65,31 @@ func NewEBlob(sCost, tCost, pCost int, password, data []byte) ([]byte, error) {
        if _, err = rand.Read(salt[:]); err != nil {
                return nil, err
        }
-       key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost)
-       kdf, err := blake2b.NewXOF(32+64, key)
-       if err != nil {
-               return nil, err
-       }
-       if _, err = kdf.Write(MagicNNCPBv2[:]); err != nil {
-               return nil, err
-       }
-       keyEnc := new([32]byte)
-       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
-       }
-       mac, err := blake2b.New256(keyAuth)
-       if err != nil {
-               return nil, err
-       }
-       chacha20.XORKeyStream(data, data, new([16]byte), keyEnc)
-       if _, err = mac.Write(data); err != nil {
-               return nil, err
-       }
-       macTag := new([blake2b.Size256]byte)
-       mac.Sum(macTag[:0])
        eblob := EBlob{
-               Magic: MagicNNCPBv2,
+               Magic: MagicNNCPBv3,
                SCost: uint32(sCost),
                TCost: uint32(tCost),
                PCost: uint32(pCost),
                Salt:  salt,
-               Blob:  data,
-               MAC:   macTag,
+               Blob:  nil,
        }
-       var eblobRaw bytes.Buffer
-       if _, err = xdr.Marshal(&eblobRaw, &eblob); err != nil {
+       var eblobBuf bytes.Buffer
+       if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
                return nil, err
        }
-       return eblobRaw.Bytes(), nil
+       key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost)
+       aead, err := chacha20poly1305.New(key)
+       if err != nil {
+               return nil, err
+       }
+       buf := make([]byte, 0, len(data)+aead.Overhead())
+       buf = aead.Seal(buf, make([]byte, aead.NonceSize()), data, eblobBuf.Bytes())
+       eblob.Blob = buf
+       eblobBuf.Reset()
+       if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
+               return nil, err
+       }
+       return eblobBuf.Bytes(), nil
 }
 
 func DeEBlob(eblobRaw, password []byte) ([]byte, error) {
@@ -117,7 +98,7 @@ func DeEBlob(eblobRaw, password []byte) ([]byte, error) {
        if _, err = xdr.Unmarshal(bytes.NewReader(eblobRaw), &eblob); err != nil {
                return nil, err
        }
-       if eblob.Magic != MagicNNCPBv2 {
+       if eblob.Magic != MagicNNCPBv3 {
                return nil, BadMagic
        }
        key := balloon.H(
@@ -128,31 +109,25 @@ func DeEBlob(eblobRaw, password []byte) ([]byte, error) {
                int(eblob.TCost),
                int(eblob.PCost),
        )
-       kdf, err := blake2b.NewXOF(32+64, key)
+       aead, err := chacha20poly1305.New(key)
        if err != nil {
                return nil, err
        }
-       if _, err = kdf.Write(MagicNNCPBv2[:]); err != nil {
-               return nil, err
-       }
-       keyEnc := new([32]byte)
-       if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
-               return nil, err
-       }
-       keyAuth := make([]byte, 64)
-       if _, err = io.ReadFull(kdf, keyAuth); err != nil {
+
+       ciphertext := eblob.Blob
+       eblob.Blob = nil
+       var eblobBuf bytes.Buffer
+       if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
                return nil, err
        }
-       mac, err := blake2b.New256(keyAuth)
+       data, err := aead.Open(
+               ciphertext[:0],
+               make([]byte, aead.NonceSize()),
+               ciphertext,
+               eblobBuf.Bytes(),
+       )
        if err != nil {
                return nil, err
        }
-       if _, err = mac.Write(eblob.Blob); err != nil {
-               return nil, err
-       }
-       if subtle.ConstantTimeCompare(mac.Sum(nil), eblob.MAC[:]) != 1 {
-               return nil, errors.New("Unauthenticated blob")
-       }
-       chacha20.XORKeyStream(eblob.Blob, eblob.Blob, new([16]byte), keyEnc)
-       return eblob.Blob, nil
+       return data, nil
 }
diff --git a/src/cypherpunks.ru/nncp/internal/.gitignore b/src/cypherpunks.ru/nncp/internal/.gitignore
deleted file mode 100644 (file)
index c1dfcb2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-chacha20
diff --git a/src/cypherpunks.ru/nncp/internal/Makefile b/src/cypherpunks.ru/nncp/internal/Makefile
deleted file mode 100644 (file)
index 2bc00c7..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-chacha20:
-       mkdir chacha20
-       cp ../../../golang.org/x/crypto/internal/chacha20/* chacha20
-       cp ../../../golang.org/x/crypto/internal/subtle/aliasing.go chacha20
-       sed -i.bak 's/package subtle.*$$/package chacha20/' chacha20/aliasing.go
-       sed -i.bak '/internal.subtle/d ; s/subtle\.//g' chacha20/chacha_generic.go
index f3aa9e976b99df64fc7be204f83b6c3420be52ad..ee7052d6d8d490228f12e512b031ca2af36ca495 100644 (file)
@@ -65,7 +65,7 @@ func (ctx *Ctx) Jobs(nodeId *NodeId, xx TRxTx) chan Job {
                                continue
                        }
                        var pktEnc PktEnc
-                       if _, err = xdr.Unmarshal(fd, &pktEnc); err != nil || pktEnc.Magic != MagicNNCPEv3 {
+                       if _, err = xdr.Unmarshal(fd, &pktEnc); err != nil || pktEnc.Magic != MagicNNCPEv4 {
                                fd.Close()
                                continue
                        }
index 1c6607f55bee938391cc238ceeda9cebe18bdebd..e157bef68b9590004c8d42f6eea5d92c02f47878 100644 (file)
@@ -20,25 +20,26 @@ package nncp
 
 import (
        "bytes"
+       "crypto/cipher"
        "crypto/rand"
-       "crypto/subtle"
        "encoding/binary"
        "errors"
        "io"
 
-       "cypherpunks.ru/nncp/internal/chacha20"
        "github.com/davecgh/go-xdr/xdr2"
        "golang.org/x/crypto/blake2b"
+       "golang.org/x/crypto/chacha20poly1305"
        "golang.org/x/crypto/curve25519"
        "golang.org/x/crypto/ed25519"
        "golang.org/x/crypto/nacl/box"
+       "golang.org/x/crypto/poly1305"
 )
 
 type PktType uint8
 
 const (
        EncBlkSize = 128 * (1 << 10)
-       KDFXOFSize = 2*(32+64) + 32
+       KDFXOFSize = chacha20poly1305.KeySize * 2
 
        PktTypeFile PktType = iota
        PktTypeFreq PktType = iota
@@ -52,12 +53,13 @@ const (
 
 var (
        MagicNNCPPv2 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 2}
-       MagicNNCPEv3 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 3}
+       MagicNNCPEv4 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 4}
        BadMagic     error   = errors.New("Unknown magic number")
        BadPktType   error   = errors.New("Unknown packet type")
 
-       PktOverhead    int64
-       PktEncOverhead int64
+       PktOverhead     int64
+       PktEncOverhead  int64
+       PktSizeOverhead int64 = 8 + poly1305.TagSize
 )
 
 type Pkt struct {
@@ -95,7 +97,7 @@ func init() {
        if err != nil {
                panic(err)
        }
-       PktOverhead = 8 + blake2b.Size256 + int64(n) + blake2b.Size256
+       PktOverhead = int64(n)
        buf.Reset()
 
        dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
@@ -103,7 +105,7 @@ func init() {
                panic(err)
        }
        pktEnc := PktEnc{
-               Magic:     MagicNNCPEv3,
+               Magic:     MagicNNCPEv4,
                Nice:      123,
                Sender:    dummyId,
                Recipient: dummyId,
@@ -132,42 +134,59 @@ func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
        return &pkt, nil
 }
 
-type DevZero struct{}
-
-func (d DevZero) Read(b []byte) (n int, err error) {
-       for n = 0; n < len(b); n++ {
-               b[n] = 0
-       }
-       return
-}
-
-func ae(keyEnc *[32]byte, r io.Reader, w io.Writer) (int, error) {
+func aeadProcess(
+       aead cipher.AEAD,
+       nonce []byte,
+       doEncrypt bool,
+       r io.Reader,
+       w io.Writer) (int, error) {
        var blkCtr uint64
-       ciphNonce := new([16]byte)
-       ciphCtr := ciphNonce[8:]
-       buf := make([]byte, EncBlkSize)
+       ciphCtr := nonce[len(nonce)-8:]
+       buf := make([]byte, EncBlkSize+aead.Overhead())
+       var toRead []byte
+       var toWrite []byte
        var n int
-       var written int
+       var readBytes int
        var err error
+       if doEncrypt {
+               toRead = buf[:EncBlkSize]
+       } else {
+               toRead = buf
+       }
        for {
-               n, err = io.ReadFull(r, buf)
+               n, err = io.ReadFull(r, toRead)
                if err != nil {
                        if err == io.EOF {
                                break
                        }
                        if err != io.ErrUnexpectedEOF {
-                               return written + n, err
+                               return readBytes + n, err
                        }
                }
-               written += n
+               readBytes += n
                blkCtr++
                binary.BigEndian.PutUint64(ciphCtr, blkCtr)
-               chacha20.XORKeyStream(buf[:n], buf[:n], ciphNonce, keyEnc)
-               if _, err = w.Write(buf[:n]); err != nil {
-                       return written, err
+               if doEncrypt {
+                       toWrite = aead.Seal(buf[:0], nonce, buf[:n], nil)
+               } else {
+                       toWrite, err = aead.Open(buf[:0], nonce, buf[:n], nil)
+                       if err != nil {
+                               return readBytes, err
+                       }
+               }
+               if _, err = w.Write(toWrite); err != nil {
+                       return readBytes, err
                }
        }
-       return written, nil
+       return readBytes, nil
+}
+
+func sizeWithTags(size int64) (fullSize int64) {
+       fullSize = size + (size/EncBlkSize)*poly1305.TagSize
+       if size%EncBlkSize != 0 {
+               fullSize += poly1305.TagSize
+       }
+       return
 }
 
 func PktEncWrite(
@@ -187,7 +206,7 @@ func PktEncWrite(
                return err
        }
        tbs := PktTbs{
-               Magic:     MagicNNCPEv3,
+               Magic:     MagicNNCPEv4,
                Nice:      nice,
                Sender:    our.Id,
                Recipient: their.Id,
@@ -200,7 +219,7 @@ func PktEncWrite(
        signature := new([ed25519.SignatureSize]byte)
        copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
        pktEnc := PktEnc{
-               Magic:     MagicNNCPEv3,
+               Magic:     MagicNNCPEv4,
                Nice:      nice,
                Sender:    our.Id,
                Recipient: their.Id,
@@ -216,71 +235,46 @@ func PktEncWrite(
        if err != nil {
                return err
        }
-       if _, err = kdf.Write(MagicNNCPEv3[:]); err != nil {
+       if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
                return err
        }
 
-       keyEnc := new([32]byte)
-       if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
+       key := make([]byte, chacha20poly1305.KeySize)
+       if _, err = io.ReadFull(kdf, key); err != nil {
                return err
        }
-       keyAuth := make([]byte, 64)
-       if _, err = io.ReadFull(kdf, keyAuth); err != nil {
-               return err
-       }
-       mac, err := blake2b.New256(keyAuth)
+       aead, err := chacha20poly1305.New(key)
        if err != nil {
                return err
        }
+       nonce := make([]byte, aead.NonceSize())
 
-       sizeBuf := make([]byte, 8)
-       binary.BigEndian.PutUint64(sizeBuf, uint64(size))
-       chacha20.XORKeyStream(sizeBuf, sizeBuf, new([16]byte), keyEnc)
-       if _, err = out.Write(sizeBuf); err != nil {
-               return err
-       }
-       if _, err = mac.Write(sizeBuf); err != nil {
-               return err
-       }
-       if _, err = out.Write(mac.Sum(nil)); err != nil {
+       fullSize := pktBuf.Len() + int(size)
+       sizeBuf := make([]byte, 8+aead.Overhead())
+       binary.BigEndian.PutUint64(sizeBuf, uint64(sizeWithTags(int64(fullSize))))
+       if _, err = out.Write(aead.Seal(sizeBuf[:0], nonce, sizeBuf[:8], nil)); err != nil {
                return err
        }
 
-       if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
-               return err
-       }
-       if _, err = io.ReadFull(kdf, keyAuth); err != nil {
-               return err
-       }
-       mac, err = blake2b.New256(keyAuth)
-       if err != nil {
-               return err
-       }
        lr := io.LimitedReader{R: data, N: size}
        mr := io.MultiReader(&pktBuf, &lr)
-       mw := io.MultiWriter(out, mac)
-       fullSize := pktBuf.Len() + int(size)
-       written, err := ae(keyEnc, mr, mw)
+       written, err := aeadProcess(aead, nonce, true, mr, out)
        if err != nil {
                return err
        }
        if written != fullSize {
                return io.ErrUnexpectedEOF
        }
-       if _, err = out.Write(mac.Sum(nil)); err != nil {
-               return err
-       }
        if padSize > 0 {
-               if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
+               if _, err = io.ReadFull(kdf, key); err != nil {
                        return err
                }
-               lr = io.LimitedReader{R: DevZero{}, N: padSize}
-               written, err = ae(keyEnc, &lr, out)
+               kdf, err = blake2b.NewXOF(blake2b.OutputLengthUnknown, key)
                if err != nil {
                        return err
                }
-               if written != int(padSize) {
-                       return io.ErrUnexpectedEOF
+               if _, err = io.CopyN(out, kdf, padSize); err != nil {
+                       return err
                }
        }
        return nil
@@ -288,7 +282,7 @@ func PktEncWrite(
 
 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
        tbs := PktTbs{
-               Magic:     MagicNNCPEv3,
+               Magic:     MagicNNCPEv4,
                Nice:      pktEnc.Nice,
                Sender:    their.Id,
                Recipient: our.Id,
@@ -311,7 +305,7 @@ func PktEncRead(
        if err != nil {
                return nil, 0, err
        }
-       if pktEnc.Magic != MagicNNCPEv3 {
+       if pktEnc.Magic != MagicNNCPEv4 {
                return nil, 0, BadMagic
        }
        their, known := nodes[*pktEnc.Sender]
@@ -334,66 +328,37 @@ func PktEncRead(
        if err != nil {
                return their, 0, err
        }
-       if _, err = kdf.Write(MagicNNCPEv3[:]); err != nil {
+       if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
                return their, 0, err
        }
 
-       keyEnc := new([32]byte)
-       if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
-               return their, 0, err
-       }
-       keyAuth := make([]byte, 64)
-       if _, err = io.ReadFull(kdf, keyAuth); err != nil {
+       key := make([]byte, chacha20poly1305.KeySize)
+       if _, err = io.ReadFull(kdf, key); err != nil {
                return their, 0, err
        }
-       mac, err := blake2b.New256(keyAuth)
+       aead, err := chacha20poly1305.New(key)
        if err != nil {
                return their, 0, err
        }
+       nonce := make([]byte, aead.NonceSize())
 
-       sizeBuf := make([]byte, 8)
+       sizeBuf := make([]byte, 8+aead.Overhead())
        if _, err = io.ReadFull(data, sizeBuf); err != nil {
                return their, 0, err
        }
-       if _, err = mac.Write(sizeBuf); err != nil {
-               return their, 0, err
-       }
-       tag := make([]byte, blake2b.Size256)
-       if _, err = io.ReadFull(data, tag); err != nil {
-               return their, 0, err
-       }
-       if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
-               return their, 0, errors.New("Unauthenticated size")
-       }
-       chacha20.XORKeyStream(sizeBuf, sizeBuf, new([16]byte), keyEnc)
-       size := int64(binary.BigEndian.Uint64(sizeBuf))
-
-       if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
-               return their, size, err
-       }
-       if _, err = io.ReadFull(kdf, keyAuth); err != nil {
-               return their, size, err
-       }
-       mac, err = blake2b.New256(keyAuth)
+       sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, nil)
        if err != nil {
                return their, 0, err
        }
+       size := int64(binary.BigEndian.Uint64(sizeBuf))
 
-       fullSize := PktOverhead + size - 8 - 2*blake2b.Size256
-       lr := io.LimitedReader{R: data, N: fullSize}
-       tr := io.TeeReader(&lr, mac)
-       written, err := ae(keyEnc, tr, out)
+       lr := io.LimitedReader{R: data, N: size}
+       written, err := aeadProcess(aead, nonce, false, &lr, out)
        if err != nil {
                return their, int64(written), err
        }
-       if written != int(fullSize) {
+       if written != int(size) {
                return their, int64(written), io.ErrUnexpectedEOF
        }
-       if _, err = io.ReadFull(data, tag); err != nil {
-               return their, size, err
-       }
-       if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
-               return their, size, errors.New("Unauthenticated payload")
-       }
        return their, size, nil
 }
index 3e3523c447deabb1ad3584d50b4f6c13a325e82e..43602d733ac199555ce11bc5d8ec26ff4ed48daa 100644 (file)
@@ -120,7 +120,7 @@ func TestPktEncRead(t *testing.T) {
                if *node.Id != *node1.Id {
                        return false
                }
-               if sizeGot != int64(size) {
+               if sizeGot != sizeWithTags(PktOverhead+int64(size)) {
                        return false
                }
                var pktBuf bytes.Buffer
index bcfeb150f7b44a50155776b65c36f678d711462c..c7522e8265b1e65e96aed9a14d4232a13f800db5 100644 (file)
@@ -37,6 +37,7 @@ import (
        "github.com/davecgh/go-xdr/xdr2"
        "github.com/dustin/go-humanize"
        "golang.org/x/crypto/blake2b"
+       "golang.org/x/crypto/poly1305"
 )
 
 const (
@@ -87,12 +88,18 @@ func (ctx *Ctx) Toss(
                var pkt Pkt
                var err error
                var pktSize int64
+               var pktSizeBlocks int64
                if _, err = xdr.Unmarshal(pipeR, &pkt); err != nil {
                        ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "unmarshal")
                        isBad = true
                        goto Closing
                }
-               pktSize = job.Size - PktEncOverhead - PktOverhead
+               pktSize = job.Size - PktEncOverhead - PktOverhead - PktSizeOverhead
+               pktSizeBlocks = pktSize / (EncBlkSize + poly1305.TagSize)
+               if pktSize%(EncBlkSize+poly1305.TagSize) != 0 {
+                       pktSize -= poly1305.TagSize
+               }
+               pktSize -= pktSizeBlocks * poly1305.TagSize
                sds["size"] = strconv.FormatInt(pktSize, 10)
                ctx.LogD("rx", sds, "taken")
                switch pkt.Type {
index b3f8da5b00031021921633415fcc133f514f676c..db260cee47d7f87914d5fbaa008185ed600562d6 100644 (file)
@@ -34,6 +34,7 @@ import (
 
        "github.com/davecgh/go-xdr/xdr2"
        "golang.org/x/crypto/blake2b"
+       "golang.org/x/crypto/chacha20poly1305"
 )
 
 func (ctx *Ctx) Tx(
@@ -53,7 +54,11 @@ func (ctx *Ctx) Tx(
                lastNode = ctx.Neigh[*node.Via[i-1]]
                hops = append(hops, lastNode)
        }
-       padSize := minSize - size - int64(len(hops))*(PktOverhead+PktEncOverhead)
+       expectedSize := size
+       for i := 0; i < len(hops); i++ {
+               expectedSize = PktEncOverhead + PktSizeOverhead + sizeWithTags(PktOverhead+expectedSize)
+       }
+       padSize := minSize - expectedSize
        if padSize < 0 {
                padSize = 0
        }
@@ -69,12 +74,11 @@ func (ctx *Ctx) Tx(
                errs <- PktEncWrite(ctx.Self, hops[0], pkt, nice, size, padSize, src, dst)
                dst.Close()
        }(curSize, src, pipeW)
-       curSize += padSize
+       curSize = PktEncOverhead + PktSizeOverhead + sizeWithTags(PktOverhead+curSize) + padSize
 
        var pipeRPrev io.Reader
        for i := 1; i < len(hops); i++ {
                pktTrns, _ := NewPkt(PktTypeTrns, 0, hops[i-1].Id[:])
-               curSize += PktOverhead + PktEncOverhead
                pipeRPrev = pipeR
                pipeR, pipeW = io.Pipe()
                go func(node *Node, pkt *Pkt, size int64, src io.Reader, dst io.WriteCloser) {
@@ -86,6 +90,7 @@ func (ctx *Ctx) Tx(
                        errs <- PktEncWrite(ctx.Self, node, pkt, nice, size, 0, src, dst)
                        dst.Close()
                }(hops[i], pktTrns, curSize, pipeRPrev, pipeW)
+               curSize = PktEncOverhead + PktSizeOverhead + sizeWithTags(PktOverhead+curSize)
        }
        go func() {
                _, err := io.Copy(tmp.W, pipeR)
@@ -116,11 +121,16 @@ func prepareTxFile(srcPath string) (io.Reader, *os.File, int64, error) {
                }
                os.Remove(src.Name())
                tmpW := bufio.NewWriter(src)
-               tmpKey := new([32]byte)
+               tmpKey := make([]byte, chacha20poly1305.KeySize)
                if _, err = rand.Read(tmpKey[:]); err != nil {
                        return nil, nil, 0, err
                }
-               written, err := ae(tmpKey, bufio.NewReader(os.Stdin), tmpW)
+               aead, err := chacha20poly1305.New(tmpKey)
+               if err != nil {
+                       return nil, nil, 0, err
+               }
+               nonce := make([]byte, aead.NonceSize())
+               written, err := aeadProcess(aead, nonce, true, bufio.NewReader(os.Stdin), tmpW)
                if err != nil {
                        return nil, nil, 0, err
                }
@@ -130,7 +140,11 @@ func prepareTxFile(srcPath string) (io.Reader, *os.File, int64, error) {
                }
                src.Seek(0, io.SeekStart)
                r, w := io.Pipe()
-               go ae(tmpKey, bufio.NewReader(src), w)
+               go func() {
+                       if _, err := aeadProcess(aead, nonce, false, bufio.NewReader(src), w); err != nil {
+                               panic(err)
+                       }
+               }()
                reader = r
        } else {
                src, err = os.Open(srcPath)