2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
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.
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.
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/>.
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"
41 EncBlkSize = 128 * (1 << 10)
42 KDFXOFSize = chacha20poly1305.KeySize * 2
44 PktTypeFile PktType = iota
45 PktTypeFreq PktType = iota
46 PktTypeExec PktType = iota
47 PktTypeTrns PktType = iota
49 MaxPathSize = 1<<8 - 1
51 NNCPBundlePrefix = "NNCP"
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")
62 PktSizeOverhead int64 = 8 + poly1305.TagSize
70 Path *[MaxPathSize]byte
87 Sign *[ed25519.SignatureSize]byte
93 Path: new([MaxPathSize]byte),
96 n, err := xdr.Marshal(&buf, pkt)
100 PktOverhead = int64(n)
103 dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
112 ExchPub: new([32]byte),
113 Sign: new([ed25519.SignatureSize]byte),
115 n, err = xdr.Marshal(&buf, pktEnc)
119 PktEncOverhead = int64(n)
122 func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
123 if len(path) > MaxPathSize {
124 return nil, errors.New("Too long path")
130 PathLen: uint8(len(path)),
131 Path: new([MaxPathSize]byte),
133 copy(pkt.Path[:], path)
142 w io.Writer) (int, error) {
144 ciphCtr := nonce[len(nonce)-8:]
145 buf := make([]byte, EncBlkSize+aead.Overhead())
152 toRead = buf[:EncBlkSize]
157 n, err = io.ReadFull(r, toRead)
162 if err != io.ErrUnexpectedEOF {
163 return readBytes + n, err
168 binary.BigEndian.PutUint64(ciphCtr, blkCtr)
170 toWrite = aead.Seal(buf[:0], nonce, buf[:n], nil)
172 toWrite, err = aead.Open(buf[:0], nonce, buf[:n], nil)
174 return readBytes, err
177 if _, err = w.Write(toWrite); err != nil {
178 return readBytes, err
181 return readBytes, nil
184 func sizeWithTags(size int64) (fullSize int64) {
185 fullSize = size + (size/EncBlkSize)*poly1305.TagSize
186 if size%EncBlkSize != 0 {
187 fullSize += poly1305.TagSize
199 out io.Writer) error {
200 pubEph, prvEph, err := box.GenerateKey(rand.Reader)
204 var pktBuf bytes.Buffer
205 if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
215 var tbsBuf bytes.Buffer
216 if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil {
219 signature := new([ed25519.SignatureSize]byte)
220 copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
229 if _, err = xdr.Marshal(out, &pktEnc); err != nil {
232 sharedKey := new([32]byte)
233 curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
234 kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
238 if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
242 key := make([]byte, chacha20poly1305.KeySize)
243 if _, err = io.ReadFull(kdf, key); err != nil {
246 aead, err := chacha20poly1305.New(key)
250 nonce := make([]byte, aead.NonceSize())
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 {
259 lr := io.LimitedReader{R: data, N: size}
260 mr := io.MultiReader(&pktBuf, &lr)
261 written, err := aeadProcess(aead, nonce, true, mr, out)
265 if written != fullSize {
266 return io.ErrUnexpectedEOF
269 if _, err = io.ReadFull(kdf, key); err != nil {
272 kdf, err = blake2b.NewXOF(blake2b.OutputLengthUnknown, key)
276 if _, err = io.CopyN(out, kdf, padSize); err != nil {
283 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
289 ExchPub: pktEnc.ExchPub,
291 var tbsBuf bytes.Buffer
292 if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
295 return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
300 nodes map[NodeId]*Node,
302 out io.Writer) (*Node, int64, error) {
304 _, err := xdr.Unmarshal(data, &pktEnc)
308 if pktEnc.Magic != MagicNNCPEv4 {
309 return nil, 0, BadMagic
311 their, known := nodes[*pktEnc.Sender]
313 return nil, 0, errors.New("Unknown sender")
315 if *pktEnc.Recipient != *our.Id {
316 return nil, 0, errors.New("Invalid recipient")
318 verified, err := TbsVerify(our, their, &pktEnc)
323 return their, 0, errors.New("Invalid signature")
325 sharedKey := new([32]byte)
326 curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
327 kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
331 if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
335 key := make([]byte, chacha20poly1305.KeySize)
336 if _, err = io.ReadFull(kdf, key); err != nil {
339 aead, err := chacha20poly1305.New(key)
343 nonce := make([]byte, aead.NonceSize())
345 sizeBuf := make([]byte, 8+aead.Overhead())
346 if _, err = io.ReadFull(data, sizeBuf); err != nil {
349 sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, nil)
353 size := int64(binary.BigEndian.Uint64(sizeBuf))
355 lr := io.LimitedReader{R: data, N: size}
356 written, err := aeadProcess(aead, nonce, false, &lr, out)
358 return their, int64(written), err
360 if written != int(size) {
361 return their, int64(written), io.ErrUnexpectedEOF
363 return their, size, nil