2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2023 Sergey Matveev <stargrave@stargrave.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 xdr "github.com/davecgh/go-xdr/xdr2"
28 "golang.org/x/crypto/chacha20poly1305"
29 "golang.org/x/crypto/curve25519"
30 "golang.org/x/crypto/ed25519"
31 "golang.org/x/crypto/nacl/box"
32 "golang.org/x/crypto/poly1305"
33 "lukechampine.com/blake3"
39 EncBlkSize = 128 * (1 << 10)
41 PktTypeFile PktType = iota
42 PktTypeFreq PktType = iota
43 PktTypeExec PktType = iota
44 PktTypeTrns PktType = iota
45 PktTypeExecFat PktType = iota
46 PktTypeArea PktType = iota
47 PktTypeACK PktType = iota
49 MaxPathSize = 1<<8 - 1
51 NNCPBundlePrefix = "NNCP"
55 BadPktType error = errors.New("Unknown packet type")
57 DeriveKeyFullCtx = string(MagicNNCPEv6.B[:]) + " FULL"
58 DeriveKeySizeCtx = string(MagicNNCPEv6.B[:]) + " SIZE"
59 DeriveKeyPadCtx = string(MagicNNCPEv6.B[:]) + " PAD"
65 TooBig = errors.New("Too big than allowed")
73 Path [MaxPathSize]byte
90 Sign [ed25519.SignatureSize]byte
98 func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
99 if len(path) > MaxPathSize {
100 return nil, errors.New("Too long path")
103 Magic: MagicNNCPPv3.B,
106 PathLen: uint8(len(path)),
108 copy(pkt.Path[:], path)
114 pkt := Pkt{Type: PktTypeFile}
115 n, err := xdr.Marshal(&buf, pkt)
119 PktOverhead = int64(n)
122 dummyId, err := NodeIdFromString(DummyB32Id)
127 Magic: MagicNNCPEv6.B,
131 n, err = xdr.Marshal(&buf, pktEnc)
135 PktEncOverhead = int64(n)
139 n, err = xdr.Marshal(&buf, size)
143 PktSizeOverhead = int64(n)
146 func ctrIncr(b []byte) {
147 for i := len(b) - 1; i >= 0; i-- {
153 panic("counter overflow")
156 func TbsPrepare(our *NodeOur, their *Node, pktEnc *PktEnc) []byte {
158 Magic: MagicNNCPEv6.B,
162 ExchPub: pktEnc.ExchPub,
164 var tbsBuf bytes.Buffer
165 if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
168 return tbsBuf.Bytes()
171 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) ([]byte, bool, error) {
172 tbs := TbsPrepare(our, their, pktEnc)
173 return tbs, ed25519.Verify(their.SignPub, tbs, pktEnc.Sign[:]), nil
176 func sizeWithTags(size int64) (fullSize int64) {
177 size += PktSizeOverhead
178 fullSize = size + (size/EncBlkSize)*poly1305.TagSize
179 if size%EncBlkSize != 0 {
180 fullSize += poly1305.TagSize
185 func sizePadCalc(sizePayload, minSize int64, wrappers int) (sizePad int64) {
186 expectedSize := sizePayload - PktOverhead
187 for i := 0; i < wrappers; i++ {
188 expectedSize = PktEncOverhead + sizeWithTags(PktOverhead+expectedSize)
190 sizePad = minSize - expectedSize
198 our *NodeOur, their *Node,
199 pkt *Pkt, nice uint8,
200 minSize, maxSize int64, wrappers int,
201 r io.Reader, w io.Writer,
202 ) (pktEncRaw []byte, size int64, err error) {
203 pub, prv, err := box.GenerateKey(rand.Reader)
209 _, err = xdr.Marshal(&buf, pkt)
213 pktRaw := make([]byte, buf.Len())
214 copy(pktRaw, buf.Bytes())
218 Magic: MagicNNCPEv6.B,
224 _, err = xdr.Marshal(&buf, &tbs)
228 signature := new([ed25519.SignatureSize]byte)
229 copy(signature[:], ed25519.Sign(our.SignPrv, buf.Bytes()))
230 ad := blake3.Sum256(buf.Bytes())
234 Magic: MagicNNCPEv6.B,
241 _, err = xdr.Marshal(&buf, &pktEnc)
245 pktEncRaw = make([]byte, buf.Len())
246 copy(pktEncRaw, buf.Bytes())
248 _, err = w.Write(pktEncRaw)
253 sharedKey := new([32]byte)
254 curve25519.ScalarMult(sharedKey, prv, their.ExchPub)
255 keyFull := make([]byte, chacha20poly1305.KeySize)
256 keySize := make([]byte, chacha20poly1305.KeySize)
257 blake3.DeriveKey(keyFull, DeriveKeyFullCtx, sharedKey[:])
258 blake3.DeriveKey(keySize, DeriveKeySizeCtx, sharedKey[:])
259 aeadFull, err := chacha20poly1305.New(keyFull)
263 aeadSize, err := chacha20poly1305.New(keySize)
267 nonce := make([]byte, aeadFull.NonceSize())
269 data := make([]byte, EncBlkSize, EncBlkSize+aeadFull.Overhead())
270 mr := io.MultiReader(bytes.NewReader(pktRaw), r)
271 var sizePayload int64
275 n, err = io.ReadFull(mr, data)
276 sizePayload += int64(n)
277 if sizePayload > maxSize {
282 ct = aeadFull.Seal(data[:0], nonce, data[:n], ad[:])
290 if !(err == io.EOF || err == io.ErrUnexpectedEOF) {
296 sizePad := sizePadCalc(sizePayload, minSize, wrappers)
297 _, err = xdr.Marshal(&buf, &PktSize{uint64(sizePayload), uint64(sizePad)})
302 var aeadLast cipher.AEAD
303 if n+int(PktSizeOverhead) > EncBlkSize {
304 left := make([]byte, (n+int(PktSizeOverhead))-EncBlkSize)
305 copy(left, data[n-len(left):])
306 copy(data[PktSizeOverhead:], data[:n-len(left)])
307 copy(data[:PktSizeOverhead], buf.Bytes())
308 ct = aeadSize.Seal(data[:0], nonce, data[:EncBlkSize], ad[:])
318 copy(data[PktSizeOverhead:], data[:n])
319 copy(data[:PktSizeOverhead], buf.Bytes())
320 n += int(PktSizeOverhead)
324 var sizeBlockPadded int
325 var sizePadLeft int64
326 if sizePad > EncBlkSize-int64(n) {
327 sizeBlockPadded = EncBlkSize
328 sizePadLeft = sizePad - (EncBlkSize - int64(n))
330 sizeBlockPadded = n + int(sizePad)
333 for i := n; i < sizeBlockPadded; i++ {
336 ct = aeadLast.Seal(data[:0], nonce, data[:sizeBlockPadded], ad[:])
344 keyPad := make([]byte, chacha20poly1305.KeySize)
345 blake3.DeriveKey(keyPad, DeriveKeyPadCtx, sharedKey[:])
346 _, err = io.CopyN(w, blake3.New(32, keyPad).XOF(), sizePadLeft)
352 our *NodeOur, nodes map[NodeId]*Node,
353 r io.Reader, w io.Writer,
354 signatureVerify bool,
355 sharedKeyCached []byte,
356 ) (sharedKey []byte, their *Node, size int64, err error) {
358 _, err = xdr.Unmarshal(r, &pktEnc)
362 switch pktEnc.Magic {
364 err = MagicNNCPEv1.TooOld()
366 err = MagicNNCPEv2.TooOld()
368 err = MagicNNCPEv3.TooOld()
370 err = MagicNNCPEv4.TooOld()
372 err = MagicNNCPEv5.TooOld()
380 if *pktEnc.Recipient != *our.Id {
381 err = errors.New("Invalid recipient")
387 their = nodes[*pktEnc.Sender]
389 err = errors.New("Unknown sender")
393 tbsRaw, verified, err = TbsVerify(our, their, &pktEnc)
398 err = errors.New("Invalid signature")
402 tbsRaw = TbsPrepare(our, &Node{Id: pktEnc.Sender}, &pktEnc)
404 ad := blake3.Sum256(tbsRaw)
405 if sharedKeyCached == nil {
407 curve25519.ScalarMult(key, our.ExchPrv, &pktEnc.ExchPub)
410 sharedKey = sharedKeyCached
413 keyFull := make([]byte, chacha20poly1305.KeySize)
414 keySize := make([]byte, chacha20poly1305.KeySize)
415 blake3.DeriveKey(keyFull, DeriveKeyFullCtx, sharedKey[:])
416 blake3.DeriveKey(keySize, DeriveKeySizeCtx, sharedKey[:])
417 aeadFull, err := chacha20poly1305.New(keyFull)
421 aeadSize, err := chacha20poly1305.New(keySize)
425 nonce := make([]byte, aeadFull.NonceSize())
427 ct := make([]byte, EncBlkSize+aeadFull.Overhead())
428 pt := make([]byte, EncBlkSize)
432 n, err = io.ReadFull(r, ct)
435 pt, err = aeadFull.Open(pt[:0], nonce, ct, ad[:])
446 case io.ErrUnexpectedEOF:
453 pt, err = aeadSize.Open(pt[:0], nonce, ct[:n], ad[:])
458 _, err = xdr.Unmarshal(bytes.NewReader(pt), &pktSize)
462 pt = pt[PktSizeOverhead:]
464 left := int64(pktSize.Payload) - size
465 for left > int64(len(pt)) {
466 size += int64(len(pt))
467 left -= int64(len(pt))
472 n, err = io.ReadFull(r, ct)
473 if err != nil && err != io.ErrUnexpectedEOF {
477 pt, err = aeadFull.Open(pt[:0], nonce, ct[:n], ad[:])
483 _, err = w.Write(pt[:left])
489 if pktSize.Pad < uint64(len(pt)) {
490 err = errors.New("unexpected pad")
493 for i := 0; i < len(pt); i++ {
495 err = errors.New("non-zero pad byte")
499 sizePad := int64(pktSize.Pad) - int64(len(pt))
504 keyPad := make([]byte, chacha20poly1305.KeySize)
505 blake3.DeriveKey(keyPad, DeriveKeyPadCtx, sharedKey[:])
506 xof := blake3.New(32, keyPad).XOF()
507 pt = make([]byte, len(ct))
509 n, err = io.ReadFull(r, ct)
510 if err != nil && err != io.ErrUnexpectedEOF {
513 _, err = io.ReadFull(xof, pt[:n])
517 if !bytes.Equal(ct[:n], pt[:n]) {
518 err = errors.New("wrong pad value")
524 err = errors.New("excess pad")