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
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
@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
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
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
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
@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
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
@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)
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 |
@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
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
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:
@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
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 {
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
}
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")
"cypherpunks.ru/nncp"
"github.com/davecgh/go-xdr/xdr2"
- "golang.org/x/crypto/blake2b"
)
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")
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")
}
}
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 {
}
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
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 (
)
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 {
PCost uint32
Salt *[32]byte
Blob []byte
- MAC *[blake2b.Size256]byte
}
func blake256() hash.Hash {
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) {
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(
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
}
+++ /dev/null
-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
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
}
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
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 {
if err != nil {
panic(err)
}
- PktOverhead = 8 + blake2b.Size256 + int64(n) + blake2b.Size256
+ PktOverhead = int64(n)
buf.Reset()
dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
panic(err)
}
pktEnc := PktEnc{
- Magic: MagicNNCPEv3,
+ Magic: MagicNNCPEv4,
Nice: 123,
Sender: dummyId,
Recipient: dummyId,
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(
return err
}
tbs := PktTbs{
- Magic: MagicNNCPEv3,
+ Magic: MagicNNCPEv4,
Nice: nice,
Sender: our.Id,
Recipient: their.Id,
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,
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
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,
if err != nil {
return nil, 0, err
}
- if pktEnc.Magic != MagicNNCPEv3 {
+ if pktEnc.Magic != MagicNNCPEv4 {
return nil, 0, BadMagic
}
their, known := nodes[*pktEnc.Sender]
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
}
if *node.Id != *node1.Id {
return false
}
- if sizeGot != int64(size) {
+ if sizeGot != sizeWithTags(PktOverhead+int64(size)) {
return false
}
var pktBuf bytes.Buffer
"github.com/davecgh/go-xdr/xdr2"
"github.com/dustin/go-humanize"
"golang.org/x/crypto/blake2b"
+ "golang.org/x/crypto/poly1305"
)
const (
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 {
"github.com/davecgh/go-xdr/xdr2"
"golang.org/x/crypto/blake2b"
+ "golang.org/x/crypto/chacha20poly1305"
)
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
}
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) {
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)
}
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
}
}
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)