]> Cypherpunks.ru repositories - nncp.git/blob - src/pkt.go
exec notification, freq.maxsize, paxed directories
[nncp.git] / src / pkt.go
1 /*
2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2019 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/blake2b"
30         "golang.org/x/crypto/chacha20poly1305"
31         "golang.org/x/crypto/curve25519"
32         "golang.org/x/crypto/ed25519"
33         "golang.org/x/crypto/nacl/box"
34         "golang.org/x/crypto/poly1305"
35 )
36
37 type PktType uint8
38
39 const (
40         EncBlkSize = 128 * (1 << 10)
41         KDFXOFSize = chacha20poly1305.KeySize * 2
42
43         PktTypeFile PktType = iota
44         PktTypeFreq PktType = iota
45         PktTypeExec PktType = iota
46         PktTypeTrns PktType = iota
47
48         MaxPathSize = 1<<8 - 1
49
50         NNCPBundlePrefix = "NNCP"
51 )
52
53 var (
54         MagicNNCPPv3 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 3}
55         MagicNNCPEv4 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 4}
56         BadMagic     error   = errors.New("Unknown magic number")
57         BadPktType   error   = errors.New("Unknown packet type")
58
59         PktOverhead     int64
60         PktEncOverhead  int64
61         PktSizeOverhead int64 = 8 + poly1305.TagSize
62 )
63
64 type Pkt struct {
65         Magic   [8]byte
66         Type    PktType
67         Nice    uint8
68         PathLen uint8
69         Path    *[MaxPathSize]byte
70 }
71
72 type PktTbs struct {
73         Magic     [8]byte
74         Nice      uint8
75         Sender    *NodeId
76         Recipient *NodeId
77         ExchPub   *[32]byte
78 }
79
80 type PktEnc struct {
81         Magic     [8]byte
82         Nice      uint8
83         Sender    *NodeId
84         Recipient *NodeId
85         ExchPub   *[32]byte
86         Sign      *[ed25519.SignatureSize]byte
87 }
88
89 func init() {
90         pkt := Pkt{
91                 Type: PktTypeFile,
92                 Path: new([MaxPathSize]byte),
93         }
94         var buf bytes.Buffer
95         n, err := xdr.Marshal(&buf, pkt)
96         if err != nil {
97                 panic(err)
98         }
99         PktOverhead = int64(n)
100         buf.Reset()
101
102         dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
103         if err != nil {
104                 panic(err)
105         }
106         pktEnc := PktEnc{
107                 Magic:     MagicNNCPEv4,
108                 Nice:      123,
109                 Sender:    dummyId,
110                 Recipient: dummyId,
111                 ExchPub:   new([32]byte),
112                 Sign:      new([ed25519.SignatureSize]byte),
113         }
114         n, err = xdr.Marshal(&buf, pktEnc)
115         if err != nil {
116                 panic(err)
117         }
118         PktEncOverhead = int64(n)
119 }
120
121 func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
122         if len(path) > MaxPathSize {
123                 return nil, errors.New("Too long path")
124         }
125         pkt := Pkt{
126                 Magic:   MagicNNCPPv3,
127                 Type:    typ,
128                 Nice:    nice,
129                 PathLen: uint8(len(path)),
130                 Path:    new([MaxPathSize]byte),
131         }
132         copy(pkt.Path[:], path)
133         return &pkt, nil
134 }
135
136 func aeadProcess(
137         aead cipher.AEAD,
138         nonce []byte,
139         doEncrypt bool,
140         r io.Reader,
141         w io.Writer,
142 ) (int, error) {
143         var blkCtr uint64
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                 blkCtr++
168                 binary.BigEndian.PutUint64(ciphCtr, blkCtr)
169                 if doEncrypt {
170                         toWrite = aead.Seal(buf[:0], nonce, buf[:n], nil)
171                 } else {
172                         toWrite, err = aead.Open(buf[:0], nonce, buf[:n], nil)
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 ) error {
201         pubEph, prvEph, err := box.GenerateKey(rand.Reader)
202         if err != nil {
203                 return err
204         }
205         var pktBuf bytes.Buffer
206         if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
207                 return err
208         }
209         tbs := PktTbs{
210                 Magic:     MagicNNCPEv4,
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 err
219         }
220         signature := new([ed25519.SignatureSize]byte)
221         copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
222         pktEnc := PktEnc{
223                 Magic:     MagicNNCPEv4,
224                 Nice:      nice,
225                 Sender:    our.Id,
226                 Recipient: their.Id,
227                 ExchPub:   pubEph,
228                 Sign:      signature,
229         }
230         if _, err = xdr.Marshal(out, &pktEnc); err != nil {
231                 return err
232         }
233         sharedKey := new([32]byte)
234         curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
235         kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
236         if err != nil {
237                 return err
238         }
239         if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
240                 return err
241         }
242
243         key := make([]byte, chacha20poly1305.KeySize)
244         if _, err = io.ReadFull(kdf, key); err != nil {
245                 return err
246         }
247         aead, err := chacha20poly1305.New(key)
248         if err != nil {
249                 return err
250         }
251         nonce := make([]byte, aead.NonceSize())
252
253         fullSize := pktBuf.Len() + int(size)
254         sizeBuf := make([]byte, 8+aead.Overhead())
255         binary.BigEndian.PutUint64(sizeBuf, uint64(sizeWithTags(int64(fullSize))))
256         if _, err = out.Write(aead.Seal(sizeBuf[:0], nonce, sizeBuf[:8], nil)); err != nil {
257                 return err
258         }
259
260         lr := io.LimitedReader{R: data, N: size}
261         mr := io.MultiReader(&pktBuf, &lr)
262         written, err := aeadProcess(aead, nonce, true, mr, out)
263         if err != nil {
264                 return err
265         }
266         if written != fullSize {
267                 return io.ErrUnexpectedEOF
268         }
269         if padSize > 0 {
270                 if _, err = io.ReadFull(kdf, key); err != nil {
271                         return err
272                 }
273                 kdf, err = blake2b.NewXOF(blake2b.OutputLengthUnknown, key)
274                 if err != nil {
275                         return err
276                 }
277                 if _, err = io.CopyN(out, kdf, padSize); err != nil {
278                         return err
279                 }
280         }
281         return nil
282 }
283
284 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
285         tbs := PktTbs{
286                 Magic:     MagicNNCPEv4,
287                 Nice:      pktEnc.Nice,
288                 Sender:    their.Id,
289                 Recipient: our.Id,
290                 ExchPub:   pktEnc.ExchPub,
291         }
292         var tbsBuf bytes.Buffer
293         if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
294                 return false, err
295         }
296         return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
297 }
298
299 func PktEncRead(
300         our *NodeOur,
301         nodes map[NodeId]*Node,
302         data io.Reader,
303         out io.Writer,
304 ) (*Node, int64, error) {
305         var pktEnc PktEnc
306         _, err := xdr.Unmarshal(data, &pktEnc)
307         if err != nil {
308                 return nil, 0, err
309         }
310         if pktEnc.Magic != MagicNNCPEv4 {
311                 return nil, 0, BadMagic
312         }
313         their, known := nodes[*pktEnc.Sender]
314         if !known {
315                 return nil, 0, errors.New("Unknown sender")
316         }
317         if *pktEnc.Recipient != *our.Id {
318                 return nil, 0, errors.New("Invalid recipient")
319         }
320         verified, err := TbsVerify(our, their, &pktEnc)
321         if err != nil {
322                 return nil, 0, err
323         }
324         if !verified {
325                 return their, 0, errors.New("Invalid signature")
326         }
327         sharedKey := new([32]byte)
328         curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
329         kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
330         if err != nil {
331                 return their, 0, err
332         }
333         if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
334                 return their, 0, err
335         }
336
337         key := make([]byte, chacha20poly1305.KeySize)
338         if _, err = io.ReadFull(kdf, key); err != nil {
339                 return their, 0, err
340         }
341         aead, err := chacha20poly1305.New(key)
342         if err != nil {
343                 return their, 0, err
344         }
345         nonce := make([]byte, aead.NonceSize())
346
347         sizeBuf := make([]byte, 8+aead.Overhead())
348         if _, err = io.ReadFull(data, sizeBuf); err != nil {
349                 return their, 0, err
350         }
351         sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, nil)
352         if err != nil {
353                 return their, 0, err
354         }
355         size := int64(binary.BigEndian.Uint64(sizeBuf))
356
357         lr := io.LimitedReader{R: data, N: size}
358         written, err := aeadProcess(aead, nonce, false, &lr, out)
359         if err != nil {
360                 return their, int64(written), err
361         }
362         if written != int(size) {
363                 return their, int64(written), io.ErrUnexpectedEOF
364         }
365         return their, size, nil
366 }