1 // NNCP -- Node to Node copy, utilities for store-and-forward data exchange
2 // Copyright (C) 2016-2024 Sergey Matveev <stargrave@stargrave.org>
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program. If not, see <http://www.gnu.org/licenses/>.
25 xdr "github.com/davecgh/go-xdr/xdr2"
26 "golang.org/x/crypto/chacha20poly1305"
27 "golang.org/x/crypto/curve25519"
28 "golang.org/x/crypto/ed25519"
29 "golang.org/x/crypto/nacl/box"
30 "golang.org/x/crypto/poly1305"
31 "lukechampine.com/blake3"
37 EncBlkSize = 128 * (1 << 10)
39 PktTypeFile PktType = iota
40 PktTypeFreq PktType = iota
41 PktTypeExec PktType = iota
42 PktTypeTrns PktType = iota
43 PktTypeExecFat PktType = iota
44 PktTypeArea PktType = iota
45 PktTypeACK PktType = iota
47 MaxPathSize = 1<<8 - 1
49 NNCPBundlePrefix = "NNCP"
53 BadPktType error = errors.New("Unknown packet type")
55 DeriveKeyFullCtx = string(MagicNNCPEv6.B[:]) + " FULL"
56 DeriveKeySizeCtx = string(MagicNNCPEv6.B[:]) + " SIZE"
57 DeriveKeyPadCtx = string(MagicNNCPEv6.B[:]) + " PAD"
63 TooBig = errors.New("Too big than allowed")
71 Path [MaxPathSize]byte
88 Sign [ed25519.SignatureSize]byte
96 func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
97 if len(path) > MaxPathSize {
98 return nil, errors.New("Too long path")
101 Magic: MagicNNCPPv3.B,
104 PathLen: uint8(len(path)),
106 copy(pkt.Path[:], path)
112 pkt := Pkt{Type: PktTypeFile}
113 n, err := xdr.Marshal(&buf, pkt)
117 PktOverhead = int64(n)
120 dummyId, err := NodeIdFromString(DummyB32Id)
125 Magic: MagicNNCPEv6.B,
129 n, err = xdr.Marshal(&buf, pktEnc)
133 PktEncOverhead = int64(n)
137 n, err = xdr.Marshal(&buf, size)
141 PktSizeOverhead = int64(n)
144 func ctrIncr(b []byte) {
145 for i := len(b) - 1; i >= 0; i-- {
151 panic("counter overflow")
154 func TbsPrepare(our *NodeOur, their *Node, pktEnc *PktEnc) []byte {
156 Magic: MagicNNCPEv6.B,
160 ExchPub: pktEnc.ExchPub,
162 var tbsBuf bytes.Buffer
163 if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
166 return tbsBuf.Bytes()
169 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) ([]byte, bool, error) {
170 tbs := TbsPrepare(our, their, pktEnc)
171 return tbs, ed25519.Verify(their.SignPub, tbs, pktEnc.Sign[:]), nil
174 func sizeWithTags(size int64) (fullSize int64) {
175 size += PktSizeOverhead
176 fullSize = size + (size/EncBlkSize)*poly1305.TagSize
177 if size%EncBlkSize != 0 {
178 fullSize += poly1305.TagSize
183 func sizePadCalc(sizePayload, minSize int64, wrappers int) (sizePad int64) {
184 expectedSize := sizePayload - PktOverhead
185 for i := 0; i < wrappers; i++ {
186 expectedSize = PktEncOverhead + sizeWithTags(PktOverhead+expectedSize)
188 sizePad = minSize - expectedSize
196 our *NodeOur, their *Node,
197 pkt *Pkt, nice uint8,
198 minSize, maxSize int64, wrappers int,
199 r io.Reader, w io.Writer,
200 ) (pktEncRaw []byte, size int64, err error) {
201 pub, prv, err := box.GenerateKey(rand.Reader)
207 _, err = xdr.Marshal(&buf, pkt)
211 pktRaw := make([]byte, buf.Len())
212 copy(pktRaw, buf.Bytes())
216 Magic: MagicNNCPEv6.B,
222 _, err = xdr.Marshal(&buf, &tbs)
226 signature := new([ed25519.SignatureSize]byte)
227 copy(signature[:], ed25519.Sign(our.SignPrv, buf.Bytes()))
228 ad := blake3.Sum256(buf.Bytes())
232 Magic: MagicNNCPEv6.B,
239 _, err = xdr.Marshal(&buf, &pktEnc)
243 pktEncRaw = make([]byte, buf.Len())
244 copy(pktEncRaw, buf.Bytes())
246 _, err = w.Write(pktEncRaw)
251 sharedKey, err := curve25519.X25519(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 key, err = curve25519.X25519(our.ExchPrv[:], pktEnc.ExchPub[:])
413 sharedKey = sharedKeyCached
416 keyFull := make([]byte, chacha20poly1305.KeySize)
417 keySize := make([]byte, chacha20poly1305.KeySize)
418 blake3.DeriveKey(keyFull, DeriveKeyFullCtx, sharedKey[:])
419 blake3.DeriveKey(keySize, DeriveKeySizeCtx, sharedKey[:])
420 aeadFull, err := chacha20poly1305.New(keyFull)
424 aeadSize, err := chacha20poly1305.New(keySize)
428 nonce := make([]byte, aeadFull.NonceSize())
430 ct := make([]byte, EncBlkSize+aeadFull.Overhead())
431 pt := make([]byte, EncBlkSize)
435 n, err = io.ReadFull(r, ct)
438 pt, err = aeadFull.Open(pt[:0], nonce, ct, ad[:])
449 case io.ErrUnexpectedEOF:
456 pt, err = aeadSize.Open(pt[:0], nonce, ct[:n], ad[:])
461 _, err = xdr.Unmarshal(bytes.NewReader(pt), &pktSize)
465 pt = pt[PktSizeOverhead:]
467 left := int64(pktSize.Payload) - size
468 for left > int64(len(pt)) {
469 size += int64(len(pt))
470 left -= int64(len(pt))
475 n, err = io.ReadFull(r, ct)
476 if err != nil && err != io.ErrUnexpectedEOF {
480 pt, err = aeadFull.Open(pt[:0], nonce, ct[:n], ad[:])
486 _, err = w.Write(pt[:left])
492 if pktSize.Pad < uint64(len(pt)) {
493 err = errors.New("unexpected pad")
496 for i := 0; i < len(pt); i++ {
498 err = errors.New("non-zero pad byte")
502 sizePad := int64(pktSize.Pad) - int64(len(pt))
507 keyPad := make([]byte, chacha20poly1305.KeySize)
508 blake3.DeriveKey(keyPad, DeriveKeyPadCtx, sharedKey[:])
509 xof := blake3.New(32, keyPad).XOF()
510 pt = make([]byte, len(ct))
512 n, err = io.ReadFull(r, ct)
513 if err != nil && err != io.ErrUnexpectedEOF {
516 _, err = io.ReadFull(xof, pt[:n])
520 if !bytes.Equal(ct[:n], pt[:n]) {
521 err = errors.New("wrong pad value")
527 err = errors.New("excess pad")