2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2017 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/hkdf"
35 "golang.org/x/crypto/nacl/box"
36 "golang.org/x/crypto/twofish"
42 PktTypeFile PktType = iota
43 PktTypeFreq PktType = iota
44 PktTypeMail PktType = iota
45 PktTypeTrns PktType = iota
47 MaxPathSize = 1<<8 - 1
55 MagicNNCPPv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 1}
56 MagicNNCPEv2 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 2}
57 BadMagic error = errors.New("Unknown magic number")
58 BadPktType error = errors.New("Unknown packet type")
68 Path *[MaxPathSize]byte
85 Sign *[ed25519.SignatureSize]byte
91 Path: new([MaxPathSize]byte),
94 n, err := xdr.Marshal(&buf, pkt)
98 PktOverhead = 8 + blake2b.Size256 + int64(n) + blake2b.Size256
101 dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
110 ExchPub: new([32]byte),
111 Sign: new([ed25519.SignatureSize]byte),
113 n, err = xdr.Marshal(&buf, pktEnc)
117 PktEncOverhead = int64(n)
120 func NewPkt(typ PktType, path string) (*Pkt, error) {
122 if len(pb) > MaxPathSize {
123 return nil, errors.New("Too long path")
128 PathLen: uint8(len(pb)),
129 Path: new([MaxPathSize]byte),
131 copy(pkt.Path[:], pb)
135 func blake256() hash.Hash {
136 h, err := blake2b.New256(nil)
143 type DevZero struct{}
145 func (d DevZero) Read(b []byte) (n int, err error) {
146 for n = 0; n < len(b); n++ {
152 func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size, padSize int64, data io.Reader, out io.Writer) error {
153 pubEph, prvEph, err := box.GenerateKey(rand.Reader)
157 var pktBuf bytes.Buffer
158 if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
168 var tbsBuf bytes.Buffer
169 if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil {
172 signature := new([ed25519.SignatureSize]byte)
173 copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
182 if _, err = xdr.Marshal(out, &pktEnc); err != nil {
185 sharedKey := new([32]byte)
186 curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
187 kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv2[:])
189 keyEnc := make([]byte, 32)
190 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
193 keyAuth := make([]byte, 64)
194 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
198 ciph, err := twofish.NewCipher(keyEnc)
202 ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
203 mac, err := blake2b.New256(keyAuth)
208 mw := io.MultiWriter(out, mac)
209 ae := &cipher.StreamWriter{S: ctr, W: mw}
210 usize := uint64(size)
211 if _, err = xdr.Marshal(ae, &usize); err != nil {
215 out.Write(mac.Sum(nil))
217 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
220 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
224 ciph, err = twofish.NewCipher(keyEnc)
228 ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
229 mac, err = blake2b.New256(keyAuth)
234 mw = io.MultiWriter(out, mac)
235 ae = &cipher.StreamWriter{S: ctr, W: mw}
236 ae.Write(pktBuf.Bytes())
237 if _, err = io.CopyN(ae, data, size); err != nil {
241 out.Write(mac.Sum(nil))
244 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
247 ciph, err = twofish.NewCipher(keyEnc)
251 ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
252 ae = &cipher.StreamWriter{S: ctr, W: out}
253 if _, err = io.CopyN(ae, DevZero{}, padSize); err != nil {
261 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
267 ExchPub: pktEnc.ExchPub,
269 var tbsBuf bytes.Buffer
270 if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
273 return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
276 func PktEncRead(our *NodeOur, nodes map[NodeId]*Node, data io.Reader, out io.Writer) (*Node, int64, error) {
278 _, err := xdr.Unmarshal(data, &pktEnc)
282 if pktEnc.Magic != MagicNNCPEv2 {
283 return nil, 0, BadMagic
285 their, known := nodes[*pktEnc.Sender]
287 return nil, 0, errors.New("Unknown sender")
289 if *pktEnc.Recipient != *our.Id {
290 return nil, 0, errors.New("Invalid recipient")
292 verified, err := TbsVerify(our, their, &pktEnc)
297 return their, 0, errors.New("Invalid signature")
299 sharedKey := new([32]byte)
300 curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
301 kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv2[:])
303 keyEnc := make([]byte, 32)
304 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
307 keyAuth := make([]byte, 64)
308 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
312 ciph, err := twofish.NewCipher(keyEnc)
316 ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
317 mac, err := blake2b.New256(keyAuth)
322 tr := io.TeeReader(data, mac)
323 ae := &cipher.StreamReader{S: ctr, R: tr}
325 if _, err = xdr.Unmarshal(ae, &usize); err != nil {
328 tag := make([]byte, blake2b.Size256)
329 if _, err = io.ReadFull(data, tag); err != nil {
332 if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
333 return their, 0, errors.New("Unauthenticated size")
337 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
338 return their, size, err
340 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
341 return their, size, err
344 ciph, err = twofish.NewCipher(keyEnc)
346 return their, size, err
348 ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
349 mac, err = blake2b.New256(keyAuth)
351 return their, size, err
354 tr = io.TeeReader(data, mac)
355 ae = &cipher.StreamReader{S: ctr, R: tr}
356 if _, err = io.CopyN(out, ae, PktOverhead+size-8-blake2b.Size256-blake2b.Size256); err != nil {
357 return their, size, err
359 if _, err = io.ReadFull(data, tag); err != nil {
360 return their, size, err
362 if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
363 return their, size, errors.New("Unauthenticated payload")
365 return their, size, nil