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