]> Cypherpunks.ru repositories - nncp.git/blob - src/pkt.go
aa3025d988b40cc73e41436a84deb36a7a3337c3
[nncp.git] / src / pkt.go
1 /*
2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2021 Sergey Matveev <stargrave@stargrave.org>
4
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.
8
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.
13
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/>.
16 */
17
18 package nncp
19
20 import (
21         "bytes"
22         "crypto/cipher"
23         "crypto/rand"
24         "encoding/binary"
25         "errors"
26         "io"
27
28         xdr "github.com/davecgh/go-xdr/xdr2"
29         "golang.org/x/crypto/chacha20poly1305"
30         "golang.org/x/crypto/curve25519"
31         "golang.org/x/crypto/ed25519"
32         "golang.org/x/crypto/nacl/box"
33         "golang.org/x/crypto/poly1305"
34         "lukechampine.com/blake3"
35 )
36
37 type PktType uint8
38
39 const (
40         EncBlkSize = 128 * (1 << 10)
41
42         PktTypeFile    PktType = iota
43         PktTypeFreq    PktType = iota
44         PktTypeExec    PktType = iota
45         PktTypeTrns    PktType = iota
46         PktTypeExecFat PktType = iota
47         PktTypeArea    PktType = iota
48
49         MaxPathSize = 1<<8 - 1
50
51         NNCPBundlePrefix = "NNCP"
52
53         PktSizeOverhead = 8 + poly1305.TagSize
54 )
55
56 var (
57         BadPktType error = errors.New("Unknown packet type")
58
59         PktOverhead    int64
60         PktEncOverhead int64
61 )
62
63 type Pkt struct {
64         Magic   [8]byte
65         Type    PktType
66         Nice    uint8
67         PathLen uint8
68         Path    [MaxPathSize]byte
69 }
70
71 type PktTbs struct {
72         Magic     [8]byte
73         Nice      uint8
74         Sender    *NodeId
75         Recipient *NodeId
76         ExchPub   [32]byte
77 }
78
79 type PktEnc struct {
80         Magic     [8]byte
81         Nice      uint8
82         Sender    *NodeId
83         Recipient *NodeId
84         ExchPub   [32]byte
85         Sign      [ed25519.SignatureSize]byte
86 }
87
88 func init() {
89         pkt := Pkt{Type: PktTypeFile}
90         var buf bytes.Buffer
91         n, err := xdr.Marshal(&buf, pkt)
92         if err != nil {
93                 panic(err)
94         }
95         PktOverhead = int64(n)
96         buf.Reset()
97
98         dummyId, err := NodeIdFromString(DummyB32Id)
99         if err != nil {
100                 panic(err)
101         }
102         pktEnc := PktEnc{
103                 Magic:     MagicNNCPEv5.B,
104                 Sender:    dummyId,
105                 Recipient: dummyId,
106         }
107         n, err = xdr.Marshal(&buf, pktEnc)
108         if err != nil {
109                 panic(err)
110         }
111         PktEncOverhead = int64(n)
112 }
113
114 func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
115         if len(path) > MaxPathSize {
116                 return nil, errors.New("Too long path")
117         }
118         pkt := Pkt{
119                 Magic:   MagicNNCPPv3.B,
120                 Type:    typ,
121                 Nice:    nice,
122                 PathLen: uint8(len(path)),
123         }
124         copy(pkt.Path[:], path)
125         return &pkt, nil
126 }
127
128 func ctrIncr(b []byte) {
129         for i := len(b) - 1; i >= 0; i-- {
130                 b[i]++
131                 if b[i] != 0 {
132                         return
133                 }
134         }
135         panic("counter overflow")
136 }
137
138 func aeadProcess(
139         aead cipher.AEAD,
140         nonce, ad []byte,
141         doEncrypt bool,
142         r io.Reader,
143         w io.Writer,
144 ) (int64, error) {
145         ciphCtr := nonce[len(nonce)-8:]
146         buf := make([]byte, EncBlkSize+aead.Overhead())
147         var toRead []byte
148         var toWrite []byte
149         var n int
150         var readBytes int64
151         var err error
152         if doEncrypt {
153                 toRead = buf[:EncBlkSize]
154         } else {
155                 toRead = buf
156         }
157         for {
158                 n, err = io.ReadFull(r, toRead)
159                 if err != nil {
160                         if err == io.EOF {
161                                 break
162                         }
163                         if err != io.ErrUnexpectedEOF {
164                                 return readBytes + int64(n), err
165                         }
166                 }
167                 readBytes += int64(n)
168                 ctrIncr(ciphCtr)
169                 if doEncrypt {
170                         toWrite = aead.Seal(buf[:0], nonce, buf[:n], ad)
171                 } else {
172                         toWrite, err = aead.Open(buf[:0], nonce, buf[:n], ad)
173                         if err != nil {
174                                 return readBytes, err
175                         }
176                 }
177                 if _, err = w.Write(toWrite); err != nil {
178                         return readBytes, err
179                 }
180         }
181         return readBytes, nil
182 }
183
184 func sizeWithTags(size int64) (fullSize int64) {
185         fullSize = size + (size/EncBlkSize)*poly1305.TagSize
186         if size%EncBlkSize != 0 {
187                 fullSize += poly1305.TagSize
188         }
189         return
190 }
191
192 func PktEncWrite(
193         our *NodeOur,
194         their *Node,
195         pkt *Pkt,
196         nice uint8,
197         size, padSize int64,
198         data io.Reader,
199         out io.Writer,
200 ) ([]byte, error) {
201         pubEph, prvEph, err := box.GenerateKey(rand.Reader)
202         if err != nil {
203                 return nil, err
204         }
205         var pktBuf bytes.Buffer
206         if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
207                 return nil, err
208         }
209         tbs := PktTbs{
210                 Magic:     MagicNNCPEv5.B,
211                 Nice:      nice,
212                 Sender:    our.Id,
213                 Recipient: their.Id,
214                 ExchPub:   *pubEph,
215         }
216         var tbsBuf bytes.Buffer
217         if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil {
218                 return nil, err
219         }
220         signature := new([ed25519.SignatureSize]byte)
221         copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
222         pktEnc := PktEnc{
223                 Magic:     MagicNNCPEv5.B,
224                 Nice:      nice,
225                 Sender:    our.Id,
226                 Recipient: their.Id,
227                 ExchPub:   *pubEph,
228                 Sign:      *signature,
229         }
230         ad := blake3.Sum256(tbsBuf.Bytes())
231         tbsBuf.Reset()
232         if _, err = xdr.Marshal(&tbsBuf, &pktEnc); err != nil {
233                 return nil, err
234         }
235         pktEncRaw := tbsBuf.Bytes()
236         if _, err = out.Write(pktEncRaw); err != nil {
237                 return nil, err
238         }
239         sharedKey := new([32]byte)
240         curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
241
242         key := make([]byte, chacha20poly1305.KeySize)
243         blake3.DeriveKey(key, string(MagicNNCPEv5.B[:]), sharedKey[:])
244         aead, err := chacha20poly1305.New(key)
245         if err != nil {
246                 return nil, err
247         }
248         nonce := make([]byte, aead.NonceSize())
249
250         fullSize := int64(pktBuf.Len()) + size
251         sizeBuf := make([]byte, 8+aead.Overhead())
252         binary.BigEndian.PutUint64(sizeBuf, uint64(sizeWithTags(fullSize)))
253         if _, err = out.Write(aead.Seal(sizeBuf[:0], nonce, sizeBuf[:8], ad[:])); err != nil {
254                 return nil, err
255         }
256
257         lr := io.LimitedReader{R: data, N: size}
258         mr := io.MultiReader(&pktBuf, &lr)
259         written, err := aeadProcess(aead, nonce, ad[:], true, mr, out)
260         if err != nil {
261                 return nil, err
262         }
263         if written != fullSize {
264                 return nil, io.ErrUnexpectedEOF
265         }
266         if padSize > 0 {
267                 blake3.DeriveKey(key, string(MagicNNCPEv5.B[:])+" PAD", sharedKey[:])
268                 xof := blake3.New(32, key).XOF()
269                 if _, err = io.CopyN(out, xof, padSize); err != nil {
270                         return nil, err
271                 }
272         }
273         return pktEncRaw, nil
274 }
275
276 func TbsPrepare(our *NodeOur, their *Node, pktEnc *PktEnc) []byte {
277         tbs := PktTbs{
278                 Magic:     MagicNNCPEv5.B,
279                 Nice:      pktEnc.Nice,
280                 Sender:    their.Id,
281                 Recipient: our.Id,
282                 ExchPub:   pktEnc.ExchPub,
283         }
284         var tbsBuf bytes.Buffer
285         if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
286                 panic(err)
287         }
288         return tbsBuf.Bytes()
289 }
290
291 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) ([]byte, bool, error) {
292         tbs := TbsPrepare(our, their, pktEnc)
293         return tbs, ed25519.Verify(their.SignPub, tbs, pktEnc.Sign[:]), nil
294 }
295
296 func PktEncRead(
297         our *NodeOur,
298         nodes map[NodeId]*Node,
299         data io.Reader,
300         out io.Writer,
301         signatureVerify bool,
302         sharedKeyCached []byte,
303 ) ([]byte, *Node, int64, error) {
304         var pktEnc PktEnc
305         _, err := xdr.Unmarshal(data, &pktEnc)
306         if err != nil {
307                 return nil, nil, 0, err
308         }
309         switch pktEnc.Magic {
310         case MagicNNCPEv1.B:
311                 err = MagicNNCPEv1.TooOld()
312         case MagicNNCPEv2.B:
313                 err = MagicNNCPEv2.TooOld()
314         case MagicNNCPEv3.B:
315                 err = MagicNNCPEv3.TooOld()
316         case MagicNNCPEv4.B:
317                 err = MagicNNCPEv4.TooOld()
318         case MagicNNCPEv5.B:
319         default:
320                 err = BadMagic
321         }
322         if err != nil {
323                 return nil, nil, 0, err
324         }
325         if *pktEnc.Recipient != *our.Id {
326                 return nil, nil, 0, errors.New("Invalid recipient")
327         }
328         var tbsRaw []byte
329         var their *Node
330         if signatureVerify {
331                 their = nodes[*pktEnc.Sender]
332                 if their == nil {
333                         return nil, nil, 0, errors.New("Unknown sender")
334                 }
335                 var verified bool
336                 tbsRaw, verified, err = TbsVerify(our, their, &pktEnc)
337                 if err != nil {
338                         return nil, nil, 0, err
339                 }
340                 if !verified {
341                         return nil, their, 0, errors.New("Invalid signature")
342                 }
343         } else {
344                 tbsRaw = TbsPrepare(our, &Node{Id: pktEnc.Sender}, &pktEnc)
345         }
346         ad := blake3.Sum256(tbsRaw)
347         sharedKey := new([32]byte)
348         if sharedKeyCached == nil {
349                 curve25519.ScalarMult(sharedKey, our.ExchPrv, &pktEnc.ExchPub)
350         } else {
351                 copy(sharedKey[:], sharedKeyCached)
352         }
353
354         key := make([]byte, chacha20poly1305.KeySize)
355         blake3.DeriveKey(key, string(MagicNNCPEv5.B[:]), sharedKey[:])
356         aead, err := chacha20poly1305.New(key)
357         if err != nil {
358                 return sharedKey[:], their, 0, err
359         }
360         nonce := make([]byte, aead.NonceSize())
361
362         sizeBuf := make([]byte, 8+aead.Overhead())
363         if _, err = io.ReadFull(data, sizeBuf); err != nil {
364                 return sharedKey[:], their, 0, err
365         }
366         sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, ad[:])
367         if err != nil {
368                 return sharedKey[:], their, 0, err
369         }
370         size := int64(binary.BigEndian.Uint64(sizeBuf))
371
372         lr := io.LimitedReader{R: data, N: size}
373         written, err := aeadProcess(aead, nonce, ad[:], false, &lr, out)
374         if err != nil {
375                 return sharedKey[:], their, written, err
376         }
377         if written != size {
378                 return sharedKey[:], their, written, io.ErrUnexpectedEOF
379         }
380         return sharedKey[:], their, size, nil
381 }