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