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