2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2018 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/>.
30 "github.com/davecgh/go-xdr/xdr2"
31 "golang.org/x/crypto/blake2b"
32 "golang.org/x/crypto/curve25519"
33 "golang.org/x/crypto/ed25519"
34 "golang.org/x/crypto/nacl/box"
40 EncBlkSize = 128 * (1 << 10)
41 KDFXOFSize = 2*(32+64) + 32
43 PktTypeFile PktType = iota
44 PktTypeFreq PktType = iota
45 PktTypeExec PktType = iota
46 PktTypeTrns PktType = iota
48 MaxPathSize = 1<<8 - 1
54 NNCPBundlePrefix = "NNCP"
58 MagicNNCPPv2 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 2}
59 MagicNNCPEv3 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 3}
60 BadMagic error = errors.New("Unknown magic number")
61 BadPktType error = errors.New("Unknown packet type")
72 Path *[MaxPathSize]byte
89 Sign *[ed25519.SignatureSize]byte
95 Path: new([MaxPathSize]byte),
98 n, err := xdr.Marshal(&buf, pkt)
102 PktOverhead = 8 + blake2b.Size256 + int64(n) + blake2b.Size256
105 dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
114 ExchPub: new([32]byte),
115 Sign: new([ed25519.SignatureSize]byte),
117 n, err = xdr.Marshal(&buf, pktEnc)
121 PktEncOverhead = int64(n)
124 func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
125 if len(path) > MaxPathSize {
126 return nil, errors.New("Too long path")
132 PathLen: uint8(len(path)),
133 Path: new([MaxPathSize]byte),
135 copy(pkt.Path[:], path)
139 type DevZero struct{}
141 func (d DevZero) Read(b []byte) (n int, err error) {
142 for n = 0; n < len(b); n++ {
148 func ae(keyEnc *[32]byte, r io.Reader, w io.Writer) (int, error) {
150 ciphNonce := new([16]byte)
151 ciphCtr := ciphNonce[8:]
152 buf := make([]byte, EncBlkSize)
157 n, err = io.ReadFull(r, buf)
162 if err != io.ErrUnexpectedEOF {
163 return written + n, err
168 binary.BigEndian.PutUint64(ciphCtr, blkCtr)
169 chacha20.XORKeyStream(buf[:n], buf[:n], ciphNonce, keyEnc)
170 if _, err = w.Write(buf[:n]); err != nil {
177 func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size, padSize int64, data io.Reader, out io.Writer) error {
178 pubEph, prvEph, err := box.GenerateKey(rand.Reader)
182 var pktBuf bytes.Buffer
183 if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
193 var tbsBuf bytes.Buffer
194 if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil {
197 signature := new([ed25519.SignatureSize]byte)
198 copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
207 if _, err = xdr.Marshal(out, &pktEnc); err != nil {
210 sharedKey := new([32]byte)
211 curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
212 kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
216 if _, err = kdf.Write(MagicNNCPEv3[:]); err != nil {
220 keyEnc := new([32]byte)
221 if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
224 keyAuth := make([]byte, 64)
225 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
228 mac, err := blake2b.New256(keyAuth)
233 sizeBuf := make([]byte, 8)
234 binary.BigEndian.PutUint64(sizeBuf, uint64(size))
235 chacha20.XORKeyStream(sizeBuf, sizeBuf, new([16]byte), keyEnc)
236 if _, err = out.Write(sizeBuf); err != nil {
239 if _, err = mac.Write(sizeBuf); err != nil {
242 if _, err = out.Write(mac.Sum(nil)); err != nil {
246 if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
249 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
252 mac, err = blake2b.New256(keyAuth)
256 lr := io.LimitedReader{R: data, N: size}
257 mr := io.MultiReader(&pktBuf, &lr)
258 mw := io.MultiWriter(out, mac)
259 fullSize := pktBuf.Len() + int(size)
260 written, err := ae(keyEnc, mr, mw)
264 if written != fullSize {
265 return io.ErrUnexpectedEOF
267 if _, err = out.Write(mac.Sum(nil)); err != nil {
271 if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
274 lr = io.LimitedReader{R: DevZero{}, N: padSize}
275 written, err = ae(keyEnc, &lr, out)
279 if written != int(padSize) {
280 return io.ErrUnexpectedEOF
286 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
292 ExchPub: pktEnc.ExchPub,
294 var tbsBuf bytes.Buffer
295 if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
298 return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
301 func PktEncRead(our *NodeOur, nodes map[NodeId]*Node, data io.Reader, out io.Writer) (*Node, int64, error) {
303 _, err := xdr.Unmarshal(data, &pktEnc)
307 if pktEnc.Magic != MagicNNCPEv3 {
308 return nil, 0, BadMagic
310 their, known := nodes[*pktEnc.Sender]
312 return nil, 0, errors.New("Unknown sender")
314 if *pktEnc.Recipient != *our.Id {
315 return nil, 0, errors.New("Invalid recipient")
317 verified, err := TbsVerify(our, their, &pktEnc)
322 return their, 0, errors.New("Invalid signature")
324 sharedKey := new([32]byte)
325 curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
326 kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
330 if _, err = kdf.Write(MagicNNCPEv3[:]); err != nil {
334 keyEnc := new([32]byte)
335 if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
338 keyAuth := make([]byte, 64)
339 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
342 mac, err := blake2b.New256(keyAuth)
347 sizeBuf := make([]byte, 8)
348 if _, err = io.ReadFull(data, sizeBuf); err != nil {
351 if _, err = mac.Write(sizeBuf); err != nil {
354 tag := make([]byte, blake2b.Size256)
355 if _, err = io.ReadFull(data, tag); err != nil {
358 if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
359 return their, 0, errors.New("Unauthenticated size")
361 chacha20.XORKeyStream(sizeBuf, sizeBuf, new([16]byte), keyEnc)
362 size := int64(binary.BigEndian.Uint64(sizeBuf))
364 if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
365 return their, size, err
367 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
368 return their, size, err
370 mac, err = blake2b.New256(keyAuth)
375 fullSize := PktOverhead + size - 8 - 2*blake2b.Size256
376 lr := io.LimitedReader{R: data, N: fullSize}
377 tr := io.TeeReader(&lr, mac)
378 written, err := ae(keyEnc, tr, out)
380 return their, int64(written), err
382 if written != int(fullSize) {
383 return their, int64(written), io.ErrUnexpectedEOF
385 if _, err = io.ReadFull(data, tag); err != nil {
386 return their, size, err
388 if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
389 return their, size, errors.New("Unauthenticated payload")
391 return their, size, nil