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 MagicNNCPEv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 1}
57 BadMagic error = errors.New("Unknown magic number")
58 BadPktType error = errors.New("Unknown packet type")
68 Path *[MaxPathSize]byte
84 Sign *[ed25519.SignatureSize]byte
90 Path: new([MaxPathSize]byte),
93 n, err := xdr.Marshal(&buf, pkt)
97 PktOverhead = 8 + blake2b.Size256 + int64(n) + blake2b.Size256
100 dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
108 ExchPub: new([32]byte),
109 Sign: new([ed25519.SignatureSize]byte),
111 n, err = xdr.Marshal(&buf, pktEnc)
115 PktEncOverhead = int64(n)
118 func NewPkt(typ PktType, path string) (*Pkt, error) {
120 if len(pb) > MaxPathSize {
121 return nil, errors.New("Too long path")
126 PathLen: uint8(len(pb)),
127 Path: new([MaxPathSize]byte),
129 copy(pkt.Path[:], pb)
133 func blake256() hash.Hash {
134 h, err := blake2b.New256(nil)
141 type DevZero struct{}
143 func (d DevZero) Read(b []byte) (n int, err error) {
144 for n = 0; n < len(b); n++ {
150 func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size, padSize int64, data io.Reader, out io.Writer) error {
151 pubEph, prvEph, err := box.GenerateKey(rand.Reader)
155 var pktBuf bytes.Buffer
156 if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
166 var tbsBuf bytes.Buffer
167 if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil {
170 signature := new([ed25519.SignatureSize]byte)
171 copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
179 if _, err = xdr.Marshal(out, &pktEnc); err != nil {
182 sharedKey := new([32]byte)
183 curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
184 kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv1[:])
186 keyEnc := make([]byte, 32)
187 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
190 keyAuth := make([]byte, 64)
191 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
195 ciph, err := twofish.NewCipher(keyEnc)
199 ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
200 mac, err := blake2b.New256(keyAuth)
205 mw := io.MultiWriter(out, mac)
206 ae := &cipher.StreamWriter{S: ctr, W: mw}
207 usize := uint64(size)
208 if _, err = xdr.Marshal(ae, &usize); err != nil {
212 out.Write(mac.Sum(nil))
214 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
217 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
221 ciph, err = twofish.NewCipher(keyEnc)
225 ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
226 mac, err = blake2b.New256(keyAuth)
231 mw = io.MultiWriter(out, mac)
232 ae = &cipher.StreamWriter{S: ctr, W: mw}
233 ae.Write(pktBuf.Bytes())
234 if _, err = io.CopyN(ae, data, size); err != nil {
238 out.Write(mac.Sum(nil))
241 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
244 ciph, err = twofish.NewCipher(keyEnc)
248 ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
249 ae = &cipher.StreamWriter{S: ctr, W: out}
250 if _, err = io.CopyN(ae, DevZero{}, padSize); err != nil {
258 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
264 ExchPub: pktEnc.ExchPub,
266 var tbsBuf bytes.Buffer
267 if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
270 return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
273 func PktEncRead(our *NodeOur, nodes map[NodeId]*Node, data io.Reader, out io.Writer) (*Node, int64, error) {
275 _, err := xdr.Unmarshal(data, &pktEnc)
279 if pktEnc.Magic != MagicNNCPEv1 {
280 return nil, 0, BadMagic
282 their, known := nodes[*pktEnc.Sender]
284 return nil, 0, errors.New("Unknown sender")
286 verified, err := TbsVerify(our, their, &pktEnc)
291 return their, 0, errors.New("Invalid signature")
293 sharedKey := new([32]byte)
294 curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
295 kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv1[:])
297 keyEnc := make([]byte, 32)
298 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
301 keyAuth := make([]byte, 64)
302 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
306 ciph, err := twofish.NewCipher(keyEnc)
310 ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
311 mac, err := blake2b.New256(keyAuth)
316 tr := io.TeeReader(data, mac)
317 ae := &cipher.StreamReader{S: ctr, R: tr}
319 if _, err = xdr.Unmarshal(ae, &usize); err != nil {
322 tag := make([]byte, blake2b.Size256)
323 if _, err = io.ReadFull(data, tag); err != nil {
326 if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
327 return their, 0, errors.New("Unauthenticated size")
331 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
332 return their, size, err
334 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
335 return their, size, err
338 ciph, err = twofish.NewCipher(keyEnc)
340 return their, size, err
342 ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
343 mac, err = blake2b.New256(keyAuth)
345 return their, size, err
348 tr = io.TeeReader(data, mac)
349 ae = &cipher.StreamReader{S: ctr, R: tr}
350 if _, err = io.CopyN(out, ae, PktOverhead+size-8-blake2b.Size256-blake2b.Size256); err != nil {
351 return their, size, err
353 if _, err = io.ReadFull(data, tag); err != nil {
354 return their, size, err
356 if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
357 return their, size, errors.New("Unauthenticated payload")
359 return their, size, nil