]> Cypherpunks.ru repositories - nncp.git/blob - src/cypherpunks.ru/nncp/pkt.go
AEAD encryption mode and new encrypted packet format
[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) (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) error {
200         pubEph, prvEph, err := box.GenerateKey(rand.Reader)
201         if err != nil {
202                 return err
203         }
204         var pktBuf bytes.Buffer
205         if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
206                 return err
207         }
208         tbs := PktTbs{
209                 Magic:     MagicNNCPEv4,
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 err
218         }
219         signature := new([ed25519.SignatureSize]byte)
220         copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
221         pktEnc := PktEnc{
222                 Magic:     MagicNNCPEv4,
223                 Nice:      nice,
224                 Sender:    our.Id,
225                 Recipient: their.Id,
226                 ExchPub:   pubEph,
227                 Sign:      signature,
228         }
229         if _, err = xdr.Marshal(out, &pktEnc); err != nil {
230                 return err
231         }
232         sharedKey := new([32]byte)
233         curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
234         kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
235         if err != nil {
236                 return err
237         }
238         if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
239                 return err
240         }
241
242         key := make([]byte, chacha20poly1305.KeySize)
243         if _, err = io.ReadFull(kdf, key); err != nil {
244                 return err
245         }
246         aead, err := chacha20poly1305.New(key)
247         if err != nil {
248                 return err
249         }
250         nonce := make([]byte, aead.NonceSize())
251
252         fullSize := pktBuf.Len() + int(size)
253         sizeBuf := make([]byte, 8+aead.Overhead())
254         binary.BigEndian.PutUint64(sizeBuf, uint64(sizeWithTags(int64(fullSize))))
255         if _, err = out.Write(aead.Seal(sizeBuf[:0], nonce, sizeBuf[:8], nil)); err != nil {
256                 return err
257         }
258
259         lr := io.LimitedReader{R: data, N: size}
260         mr := io.MultiReader(&pktBuf, &lr)
261         written, err := aeadProcess(aead, nonce, true, mr, out)
262         if err != nil {
263                 return err
264         }
265         if written != fullSize {
266                 return io.ErrUnexpectedEOF
267         }
268         if padSize > 0 {
269                 if _, err = io.ReadFull(kdf, key); err != nil {
270                         return err
271                 }
272                 kdf, err = blake2b.NewXOF(blake2b.OutputLengthUnknown, key)
273                 if err != nil {
274                         return err
275                 }
276                 if _, err = io.CopyN(out, kdf, padSize); err != nil {
277                         return err
278                 }
279         }
280         return nil
281 }
282
283 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
284         tbs := PktTbs{
285                 Magic:     MagicNNCPEv4,
286                 Nice:      pktEnc.Nice,
287                 Sender:    their.Id,
288                 Recipient: our.Id,
289                 ExchPub:   pktEnc.ExchPub,
290         }
291         var tbsBuf bytes.Buffer
292         if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
293                 return false, err
294         }
295         return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
296 }
297
298 func PktEncRead(
299         our *NodeOur,
300         nodes map[NodeId]*Node,
301         data io.Reader,
302         out io.Writer) (*Node, int64, error) {
303         var pktEnc PktEnc
304         _, err := xdr.Unmarshal(data, &pktEnc)
305         if err != nil {
306                 return nil, 0, err
307         }
308         if pktEnc.Magic != MagicNNCPEv4 {
309                 return nil, 0, BadMagic
310         }
311         their, known := nodes[*pktEnc.Sender]
312         if !known {
313                 return nil, 0, errors.New("Unknown sender")
314         }
315         if *pktEnc.Recipient != *our.Id {
316                 return nil, 0, errors.New("Invalid recipient")
317         }
318         verified, err := TbsVerify(our, their, &pktEnc)
319         if err != nil {
320                 return nil, 0, err
321         }
322         if !verified {
323                 return their, 0, errors.New("Invalid signature")
324         }
325         sharedKey := new([32]byte)
326         curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
327         kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
328         if err != nil {
329                 return their, 0, err
330         }
331         if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
332                 return their, 0, err
333         }
334
335         key := make([]byte, chacha20poly1305.KeySize)
336         if _, err = io.ReadFull(kdf, key); err != nil {
337                 return their, 0, err
338         }
339         aead, err := chacha20poly1305.New(key)
340         if err != nil {
341                 return their, 0, err
342         }
343         nonce := make([]byte, aead.NonceSize())
344
345         sizeBuf := make([]byte, 8+aead.Overhead())
346         if _, err = io.ReadFull(data, sizeBuf); err != nil {
347                 return their, 0, err
348         }
349         sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, nil)
350         if err != nil {
351                 return their, 0, err
352         }
353         size := int64(binary.BigEndian.Uint64(sizeBuf))
354
355         lr := io.LimitedReader{R: data, N: size}
356         written, err := aeadProcess(aead, nonce, false, &lr, out)
357         if err != nil {
358                 return their, int64(written), err
359         }
360         if written != int(size) {
361                 return their, int64(written), io.ErrUnexpectedEOF
362         }
363         return their, size, nil
364 }