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, err := curve25519.X25519(prv[:], their.ExchPub[:])
257 keyFull := make([]byte, chacha20poly1305.KeySize)
258 keySize := make([]byte, chacha20poly1305.KeySize)
259 blake3.DeriveKey(keyFull, DeriveKeyFullCtx, sharedKey)
260 blake3.DeriveKey(keySize, DeriveKeySizeCtx, sharedKey)
261 aeadFull, err := chacha20poly1305.New(keyFull)
265 aeadSize, err := chacha20poly1305.New(keySize)
269 nonce := make([]byte, aeadFull.NonceSize())
271 data := make([]byte, EncBlkSize, EncBlkSize+aeadFull.Overhead())
272 mr := io.MultiReader(bytes.NewReader(pktRaw), r)
273 var sizePayload int64
277 n, err = io.ReadFull(mr, data)
278 sizePayload += int64(n)
279 if sizePayload > maxSize {
284 ct = aeadFull.Seal(data[:0], nonce, data[:n], ad[:])
292 if !(err == io.EOF || err == io.ErrUnexpectedEOF) {
298 sizePad := sizePadCalc(sizePayload, minSize, wrappers)
299 _, err = xdr.Marshal(&buf, &PktSize{uint64(sizePayload), uint64(sizePad)})
304 var aeadLast cipher.AEAD
305 if n+int(PktSizeOverhead) > EncBlkSize {
306 left := make([]byte, (n+int(PktSizeOverhead))-EncBlkSize)
307 copy(left, data[n-len(left):])
308 copy(data[PktSizeOverhead:], data[:n-len(left)])
309 copy(data[:PktSizeOverhead], buf.Bytes())
310 ct = aeadSize.Seal(data[:0], nonce, data[:EncBlkSize], ad[:])
320 copy(data[PktSizeOverhead:], data[:n])
321 copy(data[:PktSizeOverhead], buf.Bytes())
322 n += int(PktSizeOverhead)
326 var sizeBlockPadded int
327 var sizePadLeft int64
328 if sizePad > EncBlkSize-int64(n) {
329 sizeBlockPadded = EncBlkSize
330 sizePadLeft = sizePad - (EncBlkSize - int64(n))
332 sizeBlockPadded = n + int(sizePad)
335 for i := n; i < sizeBlockPadded; i++ {
338 ct = aeadLast.Seal(data[:0], nonce, data[:sizeBlockPadded], ad[:])
346 keyPad := make([]byte, chacha20poly1305.KeySize)
347 blake3.DeriveKey(keyPad, DeriveKeyPadCtx, sharedKey[:])
348 _, err = io.CopyN(w, blake3.New(32, keyPad).XOF(), sizePadLeft)
354 our *NodeOur, nodes map[NodeId]*Node,
355 r io.Reader, w io.Writer,
356 signatureVerify bool,
357 sharedKeyCached []byte,
358 ) (sharedKey []byte, their *Node, size int64, err error) {
360 _, err = xdr.Unmarshal(r, &pktEnc)
364 switch pktEnc.Magic {
366 err = MagicNNCPEv1.TooOld()
368 err = MagicNNCPEv2.TooOld()
370 err = MagicNNCPEv3.TooOld()
372 err = MagicNNCPEv4.TooOld()
374 err = MagicNNCPEv5.TooOld()
382 if *pktEnc.Recipient != *our.Id {
383 err = errors.New("Invalid recipient")
389 their = nodes[*pktEnc.Sender]
391 err = errors.New("Unknown sender")
395 tbsRaw, verified, err = TbsVerify(our, their, &pktEnc)
400 err = errors.New("Invalid signature")
404 tbsRaw = TbsPrepare(our, &Node{Id: pktEnc.Sender}, &pktEnc)
406 ad := blake3.Sum256(tbsRaw)
407 if sharedKeyCached == nil {
409 key, err = curve25519.X25519(our.ExchPrv[:], pktEnc.ExchPub[:])
415 sharedKey = sharedKeyCached
418 keyFull := make([]byte, chacha20poly1305.KeySize)
419 keySize := make([]byte, chacha20poly1305.KeySize)
420 blake3.DeriveKey(keyFull, DeriveKeyFullCtx, sharedKey[:])
421 blake3.DeriveKey(keySize, DeriveKeySizeCtx, sharedKey[:])
422 aeadFull, err := chacha20poly1305.New(keyFull)
426 aeadSize, err := chacha20poly1305.New(keySize)
430 nonce := make([]byte, aeadFull.NonceSize())
432 ct := make([]byte, EncBlkSize+aeadFull.Overhead())
433 pt := make([]byte, EncBlkSize)
437 n, err = io.ReadFull(r, ct)
440 pt, err = aeadFull.Open(pt[:0], nonce, ct, ad[:])
451 case io.ErrUnexpectedEOF:
458 pt, err = aeadSize.Open(pt[:0], nonce, ct[:n], ad[:])
463 _, err = xdr.Unmarshal(bytes.NewReader(pt), &pktSize)
467 pt = pt[PktSizeOverhead:]
469 left := int64(pktSize.Payload) - size
470 for left > int64(len(pt)) {
471 size += int64(len(pt))
472 left -= int64(len(pt))
477 n, err = io.ReadFull(r, ct)
478 if err != nil && err != io.ErrUnexpectedEOF {
482 pt, err = aeadFull.Open(pt[:0], nonce, ct[:n], ad[:])
488 _, err = w.Write(pt[:left])
494 if pktSize.Pad < uint64(len(pt)) {
495 err = errors.New("unexpected pad")
498 for i := 0; i < len(pt); i++ {
500 err = errors.New("non-zero pad byte")
504 sizePad := int64(pktSize.Pad) - int64(len(pt))
509 keyPad := make([]byte, chacha20poly1305.KeySize)
510 blake3.DeriveKey(keyPad, DeriveKeyPadCtx, sharedKey[:])
511 xof := blake3.New(32, keyPad).XOF()
512 pt = make([]byte, len(ct))
514 n, err = io.ReadFull(r, ct)
515 if err != nil && err != io.ErrUnexpectedEOF {
518 _, err = io.ReadFull(xof, pt[:n])
522 if !bytes.Equal(ct[:n], pt[:n]) {
523 err = errors.New("wrong pad value")
529 err = errors.New("excess pad")