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 func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size int64, data io.Reader, out io.Writer) error {
142 pubEph, prvEph, err := box.GenerateKey(rand.Reader)
146 var pktBuf bytes.Buffer
147 if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
157 var tbsBuf bytes.Buffer
158 if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil {
161 signature := new([ed25519.SignatureSize]byte)
162 copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
170 if _, err = xdr.Marshal(out, &pktEnc); err != nil {
173 sharedKey := new([32]byte)
174 curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
175 kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv1[:])
178 keyEnc4Size := make([]byte, 32)
179 if _, err = io.ReadFull(kdf, keyEnc4Size); err != nil {
182 keyAuth4Size := make([]byte, 64)
183 if _, err = io.ReadFull(kdf, keyAuth4Size); err != nil {
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 // Initialize ciphers and MACs
196 ciph4Size, err := twofish.NewCipher(keyEnc4Size)
200 ctr4Size := cipher.NewCTR(ciph4Size, make([]byte, twofish.BlockSize))
201 mac4Size, err := blake2b.New256(keyAuth4Size)
205 ciph, err := twofish.NewCipher(keyEnc)
209 ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
210 mac, err := blake2b.New256(keyAuth)
215 mw := io.MultiWriter(out, mac4Size)
216 ae := &cipher.StreamWriter{S: ctr4Size, W: mw}
217 usize := uint64(size)
218 if _, err = xdr.Marshal(ae, &usize); err != nil {
221 out.Write(mac4Size.Sum(nil))
223 mw = io.MultiWriter(out, mac)
224 ae = &cipher.StreamWriter{S: ctr, W: mw}
225 ae.Write(pktBuf.Bytes())
226 if _, err = io.CopyN(ae, data, int64(size)); err != nil {
230 out.Write(mac.Sum(nil))
234 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
240 ExchPub: pktEnc.ExchPub,
242 var tbsBuf bytes.Buffer
243 if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
246 return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
249 func PktEncRead(our *NodeOur, nodes map[NodeId]*Node, data io.Reader, out io.Writer) (*Node, error) {
251 _, err := xdr.Unmarshal(data, &pktEnc)
255 if pktEnc.Magic != MagicNNCPEv1 {
258 their, known := nodes[*pktEnc.Sender]
260 return nil, errors.New("Unknown sender")
262 verified, err := TbsVerify(our, their, &pktEnc)
267 return their, errors.New("Invalid signature")
269 sharedKey := new([32]byte)
270 curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
271 kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv1[:])
274 keyEnc4Size := make([]byte, 32)
275 if _, err = io.ReadFull(kdf, keyEnc4Size); err != nil {
278 keyAuth4Size := make([]byte, 64)
279 if _, err = io.ReadFull(kdf, keyAuth4Size); err != nil {
282 keyEnc := make([]byte, 32)
283 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
286 keyAuth := make([]byte, 64)
287 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
291 // Initialize ciphers and MACs
292 ciph4Size, err := twofish.NewCipher(keyEnc4Size)
296 ctr4Size := cipher.NewCTR(ciph4Size, make([]byte, twofish.BlockSize))
297 mac4Size, err := blake2b.New256(keyAuth4Size)
301 ciph, err := twofish.NewCipher(keyEnc)
305 ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
306 mac, err := blake2b.New256(keyAuth)
311 tr := io.TeeReader(data, mac4Size)
312 ae := &cipher.StreamReader{S: ctr4Size, R: tr}
314 if _, err = xdr.Unmarshal(ae, &usize); err != nil {
317 tag := make([]byte, blake2b.Size256)
318 if _, err = io.ReadFull(data, tag); err != nil {
321 if subtle.ConstantTimeCompare(mac4Size.Sum(nil), tag) != 1 {
322 return their, errors.New("Unauthenticated payload")
325 tr = io.TeeReader(data, mac)
326 ae = &cipher.StreamReader{S: ctr, R: tr}
327 if _, err = io.CopyN(out, ae, PktOverhead+int64(usize)-8-blake2b.Size256-blake2b.Size256); err != nil {
330 if _, err = io.ReadFull(data, tag); err != nil {
333 if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
334 return their, errors.New("Unauthenticated payload")