2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2022 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
48 MaxPathSize = 1<<8 - 1
50 NNCPBundlePrefix = "NNCP"
54 BadPktType error = errors.New("Unknown packet type")
56 DeriveKeyFullCtx = string(MagicNNCPEv6.B[:]) + " FULL"
57 DeriveKeySizeCtx = string(MagicNNCPEv6.B[:]) + " SIZE"
58 DeriveKeyPadCtx = string(MagicNNCPEv6.B[:]) + " PAD"
64 TooBig = errors.New("Too big than allowed")
72 Path [MaxPathSize]byte
89 Sign [ed25519.SignatureSize]byte
97 func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
98 if len(path) > MaxPathSize {
99 return nil, errors.New("Too long path")
102 Magic: MagicNNCPPv3.B,
105 PathLen: uint8(len(path)),
107 copy(pkt.Path[:], path)
113 pkt := Pkt{Type: PktTypeFile}
114 n, err := xdr.Marshal(&buf, pkt)
118 PktOverhead = int64(n)
121 dummyId, err := NodeIdFromString(DummyB32Id)
126 Magic: MagicNNCPEv6.B,
130 n, err = xdr.Marshal(&buf, pktEnc)
134 PktEncOverhead = int64(n)
138 n, err = xdr.Marshal(&buf, size)
142 PktSizeOverhead = int64(n)
145 func ctrIncr(b []byte) {
146 for i := len(b) - 1; i >= 0; i-- {
152 panic("counter overflow")
155 func TbsPrepare(our *NodeOur, their *Node, pktEnc *PktEnc) []byte {
157 Magic: MagicNNCPEv6.B,
161 ExchPub: pktEnc.ExchPub,
163 var tbsBuf bytes.Buffer
164 if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
167 return tbsBuf.Bytes()
170 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) ([]byte, bool, error) {
171 tbs := TbsPrepare(our, their, pktEnc)
172 return tbs, ed25519.Verify(their.SignPub, tbs, pktEnc.Sign[:]), nil
175 func sizeWithTags(size int64) (fullSize int64) {
176 size += PktSizeOverhead
177 fullSize = size + (size/EncBlkSize)*poly1305.TagSize
178 if size%EncBlkSize != 0 {
179 fullSize += poly1305.TagSize
184 func sizePadCalc(sizePayload, minSize int64, wrappers int) (sizePad int64) {
185 expectedSize := sizePayload - PktOverhead
186 for i := 0; i < wrappers; i++ {
187 expectedSize = PktEncOverhead + sizeWithTags(PktOverhead+expectedSize)
189 sizePad = minSize - expectedSize
197 our *NodeOur, their *Node,
198 pkt *Pkt, nice uint8,
199 minSize, maxSize int64, wrappers int,
200 r io.Reader, w io.Writer,
201 ) (pktEncRaw []byte, size int64, err error) {
202 pub, prv, err := box.GenerateKey(rand.Reader)
208 _, err = xdr.Marshal(&buf, pkt)
212 pktRaw := make([]byte, buf.Len())
213 copy(pktRaw, buf.Bytes())
217 Magic: MagicNNCPEv6.B,
223 _, err = xdr.Marshal(&buf, &tbs)
227 signature := new([ed25519.SignatureSize]byte)
228 copy(signature[:], ed25519.Sign(our.SignPrv, buf.Bytes()))
229 ad := blake3.Sum256(buf.Bytes())
233 Magic: MagicNNCPEv6.B,
240 _, err = xdr.Marshal(&buf, &pktEnc)
244 pktEncRaw = make([]byte, buf.Len())
245 copy(pktEncRaw, buf.Bytes())
247 _, err = w.Write(pktEncRaw)
252 sharedKey := new([32]byte)
253 curve25519.ScalarMult(sharedKey, prv, their.ExchPub)
254 keyFull := make([]byte, chacha20poly1305.KeySize)
255 keySize := make([]byte, chacha20poly1305.KeySize)
256 blake3.DeriveKey(keyFull, DeriveKeyFullCtx, sharedKey[:])
257 blake3.DeriveKey(keySize, DeriveKeySizeCtx, sharedKey[:])
258 aeadFull, err := chacha20poly1305.New(keyFull)
262 aeadSize, err := chacha20poly1305.New(keySize)
266 nonce := make([]byte, aeadFull.NonceSize())
268 data := make([]byte, EncBlkSize, EncBlkSize+aeadFull.Overhead())
269 mr := io.MultiReader(bytes.NewReader(pktRaw), r)
270 var sizePayload int64
274 n, err = io.ReadFull(mr, data)
275 sizePayload += int64(n)
276 if sizePayload > maxSize {
281 ct = aeadFull.Seal(data[:0], nonce, data[:n], ad[:])
289 if !(err == io.EOF || err == io.ErrUnexpectedEOF) {
295 sizePad := sizePadCalc(sizePayload, minSize, wrappers)
296 _, err = xdr.Marshal(&buf, &PktSize{uint64(sizePayload), uint64(sizePad)})
301 var aeadLast cipher.AEAD
302 if n+int(PktSizeOverhead) > EncBlkSize {
303 left := make([]byte, (n+int(PktSizeOverhead))-EncBlkSize)
304 copy(left, data[n-len(left):])
305 copy(data[PktSizeOverhead:], data[:n-len(left)])
306 copy(data[:PktSizeOverhead], buf.Bytes())
307 ct = aeadSize.Seal(data[:0], nonce, data[:EncBlkSize], ad[:])
317 copy(data[PktSizeOverhead:], data[:n])
318 copy(data[:PktSizeOverhead], buf.Bytes())
319 n += int(PktSizeOverhead)
323 var sizeBlockPadded int
324 var sizePadLeft int64
325 if sizePad > EncBlkSize-int64(n) {
326 sizeBlockPadded = EncBlkSize
327 sizePadLeft = sizePad - (EncBlkSize - int64(n))
329 sizeBlockPadded = n + int(sizePad)
332 for i := n; i < sizeBlockPadded; i++ {
335 ct = aeadLast.Seal(data[:0], nonce, data[:sizeBlockPadded], ad[:])
343 keyPad := make([]byte, chacha20poly1305.KeySize)
344 blake3.DeriveKey(keyPad, DeriveKeyPadCtx, sharedKey[:])
345 _, err = io.CopyN(w, blake3.New(32, keyPad).XOF(), sizePadLeft)
351 our *NodeOur, nodes map[NodeId]*Node,
352 r io.Reader, w io.Writer,
353 signatureVerify bool,
354 sharedKeyCached []byte,
355 ) (sharedKey []byte, their *Node, size int64, err error) {
357 _, err = xdr.Unmarshal(r, &pktEnc)
361 switch pktEnc.Magic {
363 err = MagicNNCPEv1.TooOld()
365 err = MagicNNCPEv2.TooOld()
367 err = MagicNNCPEv3.TooOld()
369 err = MagicNNCPEv4.TooOld()
371 err = MagicNNCPEv5.TooOld()
379 if *pktEnc.Recipient != *our.Id {
380 err = errors.New("Invalid recipient")
386 their = nodes[*pktEnc.Sender]
388 err = errors.New("Unknown sender")
392 tbsRaw, verified, err = TbsVerify(our, their, &pktEnc)
397 err = errors.New("Invalid signature")
401 tbsRaw = TbsPrepare(our, &Node{Id: pktEnc.Sender}, &pktEnc)
403 ad := blake3.Sum256(tbsRaw)
404 if sharedKeyCached == nil {
406 curve25519.ScalarMult(key, our.ExchPrv, &pktEnc.ExchPub)
409 sharedKey = sharedKeyCached
412 keyFull := make([]byte, chacha20poly1305.KeySize)
413 keySize := make([]byte, chacha20poly1305.KeySize)
414 blake3.DeriveKey(keyFull, DeriveKeyFullCtx, sharedKey[:])
415 blake3.DeriveKey(keySize, DeriveKeySizeCtx, sharedKey[:])
416 aeadFull, err := chacha20poly1305.New(keyFull)
420 aeadSize, err := chacha20poly1305.New(keySize)
424 nonce := make([]byte, aeadFull.NonceSize())
426 ct := make([]byte, EncBlkSize+aeadFull.Overhead())
427 pt := make([]byte, EncBlkSize)
431 n, err = io.ReadFull(r, ct)
434 pt, err = aeadFull.Open(pt[:0], nonce, ct, ad[:])
445 case io.ErrUnexpectedEOF:
452 pt, err = aeadSize.Open(pt[:0], nonce, ct[:n], ad[:])
457 _, err = xdr.Unmarshal(bytes.NewReader(pt), &pktSize)
461 pt = pt[PktSizeOverhead:]
463 left := int64(pktSize.Payload) - size
464 for left > int64(len(pt)) {
465 size += int64(len(pt))
466 left -= int64(len(pt))
471 n, err = io.ReadFull(r, ct)
472 if err != nil && err != io.ErrUnexpectedEOF {
476 pt, err = aeadFull.Open(pt[:0], nonce, ct[:n], ad[:])
482 _, err = w.Write(pt[:left])
488 if pktSize.Pad < uint64(len(pt)) {
489 err = errors.New("unexpected pad")
492 for i := 0; i < len(pt); i++ {
494 err = errors.New("non-zero pad byte")
498 sizePad := int64(pktSize.Pad) - int64(len(pt))
503 keyPad := make([]byte, chacha20poly1305.KeySize)
504 blake3.DeriveKey(keyPad, DeriveKeyPadCtx, sharedKey[:])
505 xof := blake3.New(32, keyPad).XOF()
506 pt = make([]byte, len(ct))
508 n, err = io.ReadFull(r, ct)
509 if err != nil && err != io.ErrUnexpectedEOF {
512 _, err = io.ReadFull(xof, pt[:n])
516 if bytes.Compare(ct[:n], pt[:n]) != 0 {
517 err = errors.New("wrong pad value")
523 err = errors.New("excess pad")