]> Cypherpunks.ru repositories - nncp.git/blob - src/cypherpunks.ru/nncp/pkt.go
b2ab591c539a3497fb4a2d03f88f0c4e23eda93b
[nncp.git] / src / cypherpunks.ru / nncp / 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, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package nncp
20
21 import (
22         "bytes"
23         "crypto/cipher"
24         "crypto/rand"
25         "encoding/binary"
26         "errors"
27         "io"
28
29         "github.com/davecgh/go-xdr/xdr2"
30         "golang.org/x/crypto/blake2b"
31         "golang.org/x/crypto/chacha20poly1305"
32         "golang.org/x/crypto/curve25519"
33         "golang.org/x/crypto/ed25519"
34         "golang.org/x/crypto/nacl/box"
35         "golang.org/x/crypto/poly1305"
36 )
37
38 type PktType uint8
39
40 const (
41         EncBlkSize = 128 * (1 << 10)
42         KDFXOFSize = chacha20poly1305.KeySize * 2
43
44         PktTypeFile PktType = iota
45         PktTypeFreq PktType = iota
46         PktTypeExec PktType = iota
47         PktTypeTrns PktType = iota
48
49         MaxPathSize = 1<<8 - 1
50
51         NNCPBundlePrefix = "NNCP"
52 )
53
54 var (
55         MagicNNCPPv2 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 2}
56         MagicNNCPEv4 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 4}
57         BadMagic     error   = errors.New("Unknown magic number")
58         BadPktType   error   = errors.New("Unknown packet type")
59
60         PktOverhead     int64
61         PktEncOverhead  int64
62         PktSizeOverhead int64 = 8 + poly1305.TagSize
63 )
64
65 type Pkt struct {
66         Magic   [8]byte
67         Type    PktType
68         Nice    uint8
69         PathLen uint8
70         Path    *[MaxPathSize]byte
71 }
72
73 type PktTbs struct {
74         Magic     [8]byte
75         Nice      uint8
76         Sender    *NodeId
77         Recipient *NodeId
78         ExchPub   *[32]byte
79 }
80
81 type PktEnc struct {
82         Magic     [8]byte
83         Nice      uint8
84         Sender    *NodeId
85         Recipient *NodeId
86         ExchPub   *[32]byte
87         Sign      *[ed25519.SignatureSize]byte
88 }
89
90 func init() {
91         pkt := Pkt{
92                 Type: PktTypeFile,
93                 Path: new([MaxPathSize]byte),
94         }
95         var buf bytes.Buffer
96         n, err := xdr.Marshal(&buf, pkt)
97         if err != nil {
98                 panic(err)
99         }
100         PktOverhead = int64(n)
101         buf.Reset()
102
103         dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
104         if err != nil {
105                 panic(err)
106         }
107         pktEnc := PktEnc{
108                 Magic:     MagicNNCPEv4,
109                 Nice:      123,
110                 Sender:    dummyId,
111                 Recipient: dummyId,
112                 ExchPub:   new([32]byte),
113                 Sign:      new([ed25519.SignatureSize]byte),
114         }
115         n, err = xdr.Marshal(&buf, pktEnc)
116         if err != nil {
117                 panic(err)
118         }
119         PktEncOverhead = int64(n)
120 }
121
122 func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
123         if len(path) > MaxPathSize {
124                 return nil, errors.New("Too long path")
125         }
126         pkt := Pkt{
127                 Magic:   MagicNNCPPv2,
128                 Type:    typ,
129                 Nice:    nice,
130                 PathLen: uint8(len(path)),
131                 Path:    new([MaxPathSize]byte),
132         }
133         copy(pkt.Path[:], path)
134         return &pkt, nil
135 }
136
137 func aeadProcess(
138         aead cipher.AEAD,
139         nonce []byte,
140         doEncrypt bool,
141         r io.Reader,
142         w io.Writer,
143 ) (int, error) {
144         var blkCtr uint64
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 int
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 + n, err
165                         }
166                 }
167                 readBytes += n
168                 blkCtr++
169                 binary.BigEndian.PutUint64(ciphCtr, blkCtr)
170                 if doEncrypt {
171                         toWrite = aead.Seal(buf[:0], nonce, buf[:n], nil)
172                 } else {
173                         toWrite, err = aead.Open(buf[:0], nonce, buf[:n], nil)
174                         if err != nil {
175                                 return readBytes, err
176                         }
177                 }
178                 if _, err = w.Write(toWrite); err != nil {
179                         return readBytes, err
180                 }
181         }
182         return readBytes, nil
183 }
184
185 func sizeWithTags(size int64) (fullSize int64) {
186         fullSize = size + (size/EncBlkSize)*poly1305.TagSize
187         if size%EncBlkSize != 0 {
188                 fullSize += poly1305.TagSize
189         }
190         return
191 }
192
193 func PktEncWrite(
194         our *NodeOur,
195         their *Node,
196         pkt *Pkt,
197         nice uint8,
198         size, padSize int64,
199         data io.Reader,
200         out io.Writer,
201 ) error {
202         pubEph, prvEph, err := box.GenerateKey(rand.Reader)
203         if err != nil {
204                 return err
205         }
206         var pktBuf bytes.Buffer
207         if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
208                 return err
209         }
210         tbs := PktTbs{
211                 Magic:     MagicNNCPEv4,
212                 Nice:      nice,
213                 Sender:    our.Id,
214                 Recipient: their.Id,
215                 ExchPub:   pubEph,
216         }
217         var tbsBuf bytes.Buffer
218         if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil {
219                 return err
220         }
221         signature := new([ed25519.SignatureSize]byte)
222         copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
223         pktEnc := PktEnc{
224                 Magic:     MagicNNCPEv4,
225                 Nice:      nice,
226                 Sender:    our.Id,
227                 Recipient: their.Id,
228                 ExchPub:   pubEph,
229                 Sign:      signature,
230         }
231         if _, err = xdr.Marshal(out, &pktEnc); err != nil {
232                 return err
233         }
234         sharedKey := new([32]byte)
235         curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
236         kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
237         if err != nil {
238                 return err
239         }
240         if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
241                 return err
242         }
243
244         key := make([]byte, chacha20poly1305.KeySize)
245         if _, err = io.ReadFull(kdf, key); err != nil {
246                 return err
247         }
248         aead, err := chacha20poly1305.New(key)
249         if err != nil {
250                 return err
251         }
252         nonce := make([]byte, aead.NonceSize())
253
254         fullSize := pktBuf.Len() + int(size)
255         sizeBuf := make([]byte, 8+aead.Overhead())
256         binary.BigEndian.PutUint64(sizeBuf, uint64(sizeWithTags(int64(fullSize))))
257         if _, err = out.Write(aead.Seal(sizeBuf[:0], nonce, sizeBuf[:8], nil)); err != nil {
258                 return err
259         }
260
261         lr := io.LimitedReader{R: data, N: size}
262         mr := io.MultiReader(&pktBuf, &lr)
263         written, err := aeadProcess(aead, nonce, true, mr, out)
264         if err != nil {
265                 return err
266         }
267         if written != fullSize {
268                 return io.ErrUnexpectedEOF
269         }
270         if padSize > 0 {
271                 if _, err = io.ReadFull(kdf, key); err != nil {
272                         return err
273                 }
274                 kdf, err = blake2b.NewXOF(blake2b.OutputLengthUnknown, key)
275                 if err != nil {
276                         return err
277                 }
278                 if _, err = io.CopyN(out, kdf, padSize); err != nil {
279                         return err
280                 }
281         }
282         return nil
283 }
284
285 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
286         tbs := PktTbs{
287                 Magic:     MagicNNCPEv4,
288                 Nice:      pktEnc.Nice,
289                 Sender:    their.Id,
290                 Recipient: our.Id,
291                 ExchPub:   pktEnc.ExchPub,
292         }
293         var tbsBuf bytes.Buffer
294         if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
295                 return false, err
296         }
297         return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
298 }
299
300 func PktEncRead(
301         our *NodeOur,
302         nodes map[NodeId]*Node,
303         data io.Reader,
304         out io.Writer,
305 ) (*Node, int64, error) {
306         var pktEnc PktEnc
307         _, err := xdr.Unmarshal(data, &pktEnc)
308         if err != nil {
309                 return nil, 0, err
310         }
311         if pktEnc.Magic != MagicNNCPEv4 {
312                 return nil, 0, BadMagic
313         }
314         their, known := nodes[*pktEnc.Sender]
315         if !known {
316                 return nil, 0, errors.New("Unknown sender")
317         }
318         if *pktEnc.Recipient != *our.Id {
319                 return nil, 0, errors.New("Invalid recipient")
320         }
321         verified, err := TbsVerify(our, their, &pktEnc)
322         if err != nil {
323                 return nil, 0, err
324         }
325         if !verified {
326                 return their, 0, errors.New("Invalid signature")
327         }
328         sharedKey := new([32]byte)
329         curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
330         kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
331         if err != nil {
332                 return their, 0, err
333         }
334         if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
335                 return their, 0, err
336         }
337
338         key := make([]byte, chacha20poly1305.KeySize)
339         if _, err = io.ReadFull(kdf, key); err != nil {
340                 return their, 0, err
341         }
342         aead, err := chacha20poly1305.New(key)
343         if err != nil {
344                 return their, 0, err
345         }
346         nonce := make([]byte, aead.NonceSize())
347
348         sizeBuf := make([]byte, 8+aead.Overhead())
349         if _, err = io.ReadFull(data, sizeBuf); err != nil {
350                 return their, 0, err
351         }
352         sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, nil)
353         if err != nil {
354                 return their, 0, err
355         }
356         size := int64(binary.BigEndian.Uint64(sizeBuf))
357
358         lr := io.LimitedReader{R: data, N: size}
359         written, err := aeadProcess(aead, nonce, false, &lr, out)
360         if err != nil {
361                 return their, int64(written), err
362         }
363         if written != int(size) {
364                 return their, int64(written), io.ErrUnexpectedEOF
365         }
366         return their, size, nil
367 }