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
53 NNCPBundlePrefix = "NNCP"
57 MagicNNCPPv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 1}
58 MagicNNCPEv2 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 2}
59 BadMagic error = errors.New("Unknown magic number")
60 BadPktType error = errors.New("Unknown packet type")
70 Path *[MaxPathSize]byte
87 Sign *[ed25519.SignatureSize]byte
93 Path: new([MaxPathSize]byte),
96 n, err := xdr.Marshal(&buf, pkt)
100 PktOverhead = 8 + blake2b.Size256 + int64(n) + blake2b.Size256
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, path string) (*Pkt, error) {
124 if len(pb) > MaxPathSize {
125 return nil, errors.New("Too long path")
130 PathLen: uint8(len(pb)),
131 Path: new([MaxPathSize]byte),
133 copy(pkt.Path[:], pb)
137 func blake256() hash.Hash {
138 h, err := blake2b.New256(nil)
145 type DevZero struct{}
147 func (d DevZero) Read(b []byte) (n int, err error) {
148 for n = 0; n < len(b); n++ {
154 func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size, padSize int64, data io.Reader, out io.Writer) error {
155 pubEph, prvEph, err := box.GenerateKey(rand.Reader)
159 var pktBuf bytes.Buffer
160 if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
170 var tbsBuf bytes.Buffer
171 if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil {
174 signature := new([ed25519.SignatureSize]byte)
175 copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
184 if _, err = xdr.Marshal(out, &pktEnc); err != nil {
187 sharedKey := new([32]byte)
188 curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
189 kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv2[:])
191 keyEnc := make([]byte, 32)
192 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
195 keyAuth := make([]byte, 64)
196 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
200 ciph, err := twofish.NewCipher(keyEnc)
204 ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
205 mac, err := blake2b.New256(keyAuth)
210 mw := io.MultiWriter(out, mac)
211 ae := &cipher.StreamWriter{S: ctr, W: mw}
212 usize := uint64(size)
213 if _, err = xdr.Marshal(ae, &usize); err != nil {
217 out.Write(mac.Sum(nil))
219 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
222 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
226 ciph, err = twofish.NewCipher(keyEnc)
230 ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
231 mac, err = blake2b.New256(keyAuth)
236 mw = io.MultiWriter(out, mac)
237 ae = &cipher.StreamWriter{S: ctr, W: mw}
238 ae.Write(pktBuf.Bytes())
239 if _, err = io.CopyN(ae, data, size); err != nil {
243 out.Write(mac.Sum(nil))
246 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
249 ciph, err = twofish.NewCipher(keyEnc)
253 ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
254 ae = &cipher.StreamWriter{S: ctr, W: out}
255 if _, err = io.CopyN(ae, DevZero{}, padSize); err != nil {
263 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
269 ExchPub: pktEnc.ExchPub,
271 var tbsBuf bytes.Buffer
272 if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
275 return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
278 func PktEncRead(our *NodeOur, nodes map[NodeId]*Node, data io.Reader, out io.Writer) (*Node, int64, error) {
280 _, err := xdr.Unmarshal(data, &pktEnc)
284 if pktEnc.Magic != MagicNNCPEv2 {
285 return nil, 0, BadMagic
287 their, known := nodes[*pktEnc.Sender]
289 return nil, 0, errors.New("Unknown sender")
291 if *pktEnc.Recipient != *our.Id {
292 return nil, 0, errors.New("Invalid recipient")
294 verified, err := TbsVerify(our, their, &pktEnc)
299 return their, 0, errors.New("Invalid signature")
301 sharedKey := new([32]byte)
302 curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
303 kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv2[:])
305 keyEnc := make([]byte, 32)
306 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
309 keyAuth := make([]byte, 64)
310 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
314 ciph, err := twofish.NewCipher(keyEnc)
318 ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
319 mac, err := blake2b.New256(keyAuth)
324 tr := io.TeeReader(data, mac)
325 ae := &cipher.StreamReader{S: ctr, R: tr}
327 if _, err = xdr.Unmarshal(ae, &usize); err != nil {
330 tag := make([]byte, blake2b.Size256)
331 if _, err = io.ReadFull(data, tag); err != nil {
334 if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
335 return their, 0, errors.New("Unauthenticated size")
339 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
340 return their, size, err
342 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
343 return their, size, err
346 ciph, err = twofish.NewCipher(keyEnc)
348 return their, size, err
350 ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
351 mac, err = blake2b.New256(keyAuth)
353 return their, size, err
356 tr = io.TeeReader(data, mac)
357 ae = &cipher.StreamReader{S: ctr, R: tr}
358 if _, err = io.CopyN(out, ae, PktOverhead+size-8-blake2b.Size256-blake2b.Size256); err != nil {
359 return their, size, err
361 if _, err = io.ReadFull(data, tag); err != nil {
362 return their, size, err
364 if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
365 return their, size, errors.New("Unauthenticated payload")
367 return their, size, nil