]> Cypherpunks.ru repositories - nncp.git/blob - src/pkt.go
Unify copyright comment format
[nncp.git] / src / pkt.go
1 // NNCP -- Node to Node copy, utilities for store-and-forward data exchange
2 // Copyright (C) 2016-2024 Sergey Matveev <stargrave@stargrave.org>
3 //
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.
7 //
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.
12 //
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/>.
15
16 package nncp
17
18 import (
19         "bytes"
20         "crypto/cipher"
21         "crypto/rand"
22         "errors"
23         "io"
24
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"
32 )
33
34 type PktType uint8
35
36 const (
37         EncBlkSize = 128 * (1 << 10)
38
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
46
47         MaxPathSize = 1<<8 - 1
48
49         NNCPBundlePrefix = "NNCP"
50 )
51
52 var (
53         BadPktType error = errors.New("Unknown packet type")
54
55         DeriveKeyFullCtx = string(MagicNNCPEv6.B[:]) + " FULL"
56         DeriveKeySizeCtx = string(MagicNNCPEv6.B[:]) + " SIZE"
57         DeriveKeyPadCtx  = string(MagicNNCPEv6.B[:]) + " PAD"
58
59         PktOverhead     int64
60         PktEncOverhead  int64
61         PktSizeOverhead int64
62
63         TooBig = errors.New("Too big than allowed")
64 )
65
66 type Pkt struct {
67         Magic   [8]byte
68         Type    PktType
69         Nice    uint8
70         PathLen uint8
71         Path    [MaxPathSize]byte
72 }
73
74 type PktTbs struct {
75         Magic     [8]byte
76         Nice      uint8
77         Sender    *NodeId
78         Recipient *NodeId
79         ExchPub   [32]byte
80 }
81
82 type PktEnc struct {
83         Magic     [8]byte
84         Nice      uint8
85         Sender    *NodeId
86         Recipient *NodeId
87         ExchPub   [32]byte
88         Sign      [ed25519.SignatureSize]byte
89 }
90
91 type PktSize struct {
92         Payload uint64
93         Pad     uint64
94 }
95
96 func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
97         if len(path) > MaxPathSize {
98                 return nil, errors.New("Too long path")
99         }
100         pkt := Pkt{
101                 Magic:   MagicNNCPPv3.B,
102                 Type:    typ,
103                 Nice:    nice,
104                 PathLen: uint8(len(path)),
105         }
106         copy(pkt.Path[:], path)
107         return &pkt, nil
108 }
109
110 func init() {
111         var buf bytes.Buffer
112         pkt := Pkt{Type: PktTypeFile}
113         n, err := xdr.Marshal(&buf, pkt)
114         if err != nil {
115                 panic(err)
116         }
117         PktOverhead = int64(n)
118         buf.Reset()
119
120         dummyId, err := NodeIdFromString(DummyB32Id)
121         if err != nil {
122                 panic(err)
123         }
124         pktEnc := PktEnc{
125                 Magic:     MagicNNCPEv6.B,
126                 Sender:    dummyId,
127                 Recipient: dummyId,
128         }
129         n, err = xdr.Marshal(&buf, pktEnc)
130         if err != nil {
131                 panic(err)
132         }
133         PktEncOverhead = int64(n)
134         buf.Reset()
135
136         size := PktSize{}
137         n, err = xdr.Marshal(&buf, size)
138         if err != nil {
139                 panic(err)
140         }
141         PktSizeOverhead = int64(n)
142 }
143
144 func ctrIncr(b []byte) {
145         for i := len(b) - 1; i >= 0; i-- {
146                 b[i]++
147                 if b[i] != 0 {
148                         return
149                 }
150         }
151         panic("counter overflow")
152 }
153
154 func TbsPrepare(our *NodeOur, their *Node, pktEnc *PktEnc) []byte {
155         tbs := PktTbs{
156                 Magic:     MagicNNCPEv6.B,
157                 Nice:      pktEnc.Nice,
158                 Sender:    their.Id,
159                 Recipient: our.Id,
160                 ExchPub:   pktEnc.ExchPub,
161         }
162         var tbsBuf bytes.Buffer
163         if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
164                 panic(err)
165         }
166         return tbsBuf.Bytes()
167 }
168
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
172 }
173
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
179         }
180         return
181 }
182
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)
187         }
188         sizePad = minSize - expectedSize
189         if sizePad < 0 {
190                 sizePad = 0
191         }
192         return
193 }
194
195 func PktEncWrite(
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)
202         if err != nil {
203                 return nil, 0, err
204         }
205
206         var buf bytes.Buffer
207         _, err = xdr.Marshal(&buf, pkt)
208         if err != nil {
209                 return
210         }
211         pktRaw := make([]byte, buf.Len())
212         copy(pktRaw, buf.Bytes())
213         buf.Reset()
214
215         tbs := PktTbs{
216                 Magic:     MagicNNCPEv6.B,
217                 Nice:      nice,
218                 Sender:    our.Id,
219                 Recipient: their.Id,
220                 ExchPub:   *pub,
221         }
222         _, err = xdr.Marshal(&buf, &tbs)
223         if err != nil {
224                 return
225         }
226         signature := new([ed25519.SignatureSize]byte)
227         copy(signature[:], ed25519.Sign(our.SignPrv, buf.Bytes()))
228         ad := blake3.Sum256(buf.Bytes())
229         buf.Reset()
230
231         pktEnc := PktEnc{
232                 Magic:     MagicNNCPEv6.B,
233                 Nice:      nice,
234                 Sender:    our.Id,
235                 Recipient: their.Id,
236                 ExchPub:   *pub,
237                 Sign:      *signature,
238         }
239         _, err = xdr.Marshal(&buf, &pktEnc)
240         if err != nil {
241                 return
242         }
243         pktEncRaw = make([]byte, buf.Len())
244         copy(pktEncRaw, buf.Bytes())
245         buf.Reset()
246         _, err = w.Write(pktEncRaw)
247         if err != nil {
248                 return
249         }
250
251         sharedKey, err := curve25519.X25519(prv[:], their.ExchPub[:])
252         if err != nil {
253                 return
254         }
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)
260         if err != nil {
261                 return
262         }
263         aeadSize, err := chacha20poly1305.New(keySize)
264         if err != nil {
265                 return
266         }
267         nonce := make([]byte, aeadFull.NonceSize())
268
269         data := make([]byte, EncBlkSize, EncBlkSize+aeadFull.Overhead())
270         mr := io.MultiReader(bytes.NewReader(pktRaw), r)
271         var sizePayload int64
272         var n int
273         var ct []byte
274         for {
275                 n, err = io.ReadFull(mr, data)
276                 sizePayload += int64(n)
277                 if sizePayload > maxSize {
278                         err = TooBig
279                         return
280                 }
281                 if err == nil {
282                         ct = aeadFull.Seal(data[:0], nonce, data[:n], ad[:])
283                         _, err = w.Write(ct)
284                         if err != nil {
285                                 return
286                         }
287                         ctrIncr(nonce)
288                         continue
289                 }
290                 if !(err == io.EOF || err == io.ErrUnexpectedEOF) {
291                         return
292                 }
293                 break
294         }
295
296         sizePad := sizePadCalc(sizePayload, minSize, wrappers)
297         _, err = xdr.Marshal(&buf, &PktSize{uint64(sizePayload), uint64(sizePad)})
298         if err != nil {
299                 return
300         }
301
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[:])
309                 _, err = w.Write(ct)
310                 if err != nil {
311                         return
312                 }
313                 ctrIncr(nonce)
314                 copy(data, left)
315                 n = len(left)
316                 aeadLast = aeadFull
317         } else {
318                 copy(data[PktSizeOverhead:], data[:n])
319                 copy(data[:PktSizeOverhead], buf.Bytes())
320                 n += int(PktSizeOverhead)
321                 aeadLast = aeadSize
322         }
323
324         var sizeBlockPadded int
325         var sizePadLeft int64
326         if sizePad > EncBlkSize-int64(n) {
327                 sizeBlockPadded = EncBlkSize
328                 sizePadLeft = sizePad - (EncBlkSize - int64(n))
329         } else {
330                 sizeBlockPadded = n + int(sizePad)
331                 sizePadLeft = 0
332         }
333         for i := n; i < sizeBlockPadded; i++ {
334                 data[i] = 0
335         }
336         ct = aeadLast.Seal(data[:0], nonce, data[:sizeBlockPadded], ad[:])
337         _, err = w.Write(ct)
338         if err != nil {
339                 return
340         }
341
342         size = sizePayload
343         if sizePadLeft > 0 {
344                 keyPad := make([]byte, chacha20poly1305.KeySize)
345                 blake3.DeriveKey(keyPad, DeriveKeyPadCtx, sharedKey[:])
346                 _, err = io.CopyN(w, blake3.New(32, keyPad).XOF(), sizePadLeft)
347         }
348         return
349 }
350
351 func PktEncRead(
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) {
357         var pktEnc PktEnc
358         _, err = xdr.Unmarshal(r, &pktEnc)
359         if err != nil {
360                 return
361         }
362         switch pktEnc.Magic {
363         case MagicNNCPEv1.B:
364                 err = MagicNNCPEv1.TooOld()
365         case MagicNNCPEv2.B:
366                 err = MagicNNCPEv2.TooOld()
367         case MagicNNCPEv3.B:
368                 err = MagicNNCPEv3.TooOld()
369         case MagicNNCPEv4.B:
370                 err = MagicNNCPEv4.TooOld()
371         case MagicNNCPEv5.B:
372                 err = MagicNNCPEv5.TooOld()
373         case MagicNNCPEv6.B:
374         default:
375                 err = BadMagic
376         }
377         if err != nil {
378                 return
379         }
380         if *pktEnc.Recipient != *our.Id {
381                 err = errors.New("Invalid recipient")
382                 return
383         }
384
385         var tbsRaw []byte
386         if signatureVerify {
387                 their = nodes[*pktEnc.Sender]
388                 if their == nil {
389                         err = errors.New("Unknown sender")
390                         return
391                 }
392                 var verified bool
393                 tbsRaw, verified, err = TbsVerify(our, their, &pktEnc)
394                 if err != nil {
395                         return
396                 }
397                 if !verified {
398                         err = errors.New("Invalid signature")
399                         return
400                 }
401         } else {
402                 tbsRaw = TbsPrepare(our, &Node{Id: pktEnc.Sender}, &pktEnc)
403         }
404         ad := blake3.Sum256(tbsRaw)
405         if sharedKeyCached == nil {
406                 var key []byte
407                 key, err = curve25519.X25519(our.ExchPrv[:], pktEnc.ExchPub[:])
408                 if err != nil {
409                         return
410                 }
411                 sharedKey = key[:]
412         } else {
413                 sharedKey = sharedKeyCached
414         }
415
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)
421         if err != nil {
422                 return
423         }
424         aeadSize, err := chacha20poly1305.New(keySize)
425         if err != nil {
426                 return
427         }
428         nonce := make([]byte, aeadFull.NonceSize())
429
430         ct := make([]byte, EncBlkSize+aeadFull.Overhead())
431         pt := make([]byte, EncBlkSize)
432         var n int
433 FullRead:
434         for {
435                 n, err = io.ReadFull(r, ct)
436                 switch err {
437                 case nil:
438                         pt, err = aeadFull.Open(pt[:0], nonce, ct, ad[:])
439                         if err != nil {
440                                 break FullRead
441                         }
442                         size += EncBlkSize
443                         _, err = w.Write(pt)
444                         if err != nil {
445                                 return
446                         }
447                         ctrIncr(nonce)
448                         continue
449                 case io.ErrUnexpectedEOF:
450                         break FullRead
451                 default:
452                         return
453                 }
454         }
455
456         pt, err = aeadSize.Open(pt[:0], nonce, ct[:n], ad[:])
457         if err != nil {
458                 return
459         }
460         var pktSize PktSize
461         _, err = xdr.Unmarshal(bytes.NewReader(pt), &pktSize)
462         if err != nil {
463                 return
464         }
465         pt = pt[PktSizeOverhead:]
466
467         left := int64(pktSize.Payload) - size
468         for left > int64(len(pt)) {
469                 size += int64(len(pt))
470                 left -= int64(len(pt))
471                 _, err = w.Write(pt)
472                 if err != nil {
473                         return
474                 }
475                 n, err = io.ReadFull(r, ct)
476                 if err != nil && err != io.ErrUnexpectedEOF {
477                         return
478                 }
479                 ctrIncr(nonce)
480                 pt, err = aeadFull.Open(pt[:0], nonce, ct[:n], ad[:])
481                 if err != nil {
482                         return
483                 }
484         }
485         size += left
486         _, err = w.Write(pt[:left])
487         if err != nil {
488                 return
489         }
490         pt = pt[left:]
491
492         if pktSize.Pad < uint64(len(pt)) {
493                 err = errors.New("unexpected pad")
494                 return
495         }
496         for i := 0; i < len(pt); i++ {
497                 if pt[i] != 0 {
498                         err = errors.New("non-zero pad byte")
499                         return
500                 }
501         }
502         sizePad := int64(pktSize.Pad) - int64(len(pt))
503         if sizePad == 0 {
504                 return
505         }
506
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))
511         for sizePad > 0 {
512                 n, err = io.ReadFull(r, ct)
513                 if err != nil && err != io.ErrUnexpectedEOF {
514                         return
515                 }
516                 _, err = io.ReadFull(xof, pt[:n])
517                 if err != nil {
518                         panic(err)
519                 }
520                 if !bytes.Equal(ct[:n], pt[:n]) {
521                         err = errors.New("wrong pad value")
522                         return
523                 }
524                 sizePad -= int64(n)
525         }
526         if sizePad < 0 {
527                 err = errors.New("excess pad")
528         }
529         return
530 }