2 NNCP -- Node-to-Node CoPy
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', 1, 0, 0}
56 MagicNNCPEv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 1, 0, 0}
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
92 Path: new([MaxPathSize]byte),
95 n, err := xdr.Marshal(&buf, pkt)
99 PktOverhead = int64(n) + blake2b.Size256
102 dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
110 ExchPub: new([32]byte),
111 Sign: new([ed25519.SignatureSize]byte),
114 n, err = xdr.Marshal(&buf, pktEnc)
118 PktEncOverhead = int64(n)
121 func NewPkt(typ PktType, path string) (*Pkt, error) {
123 if len(pb) > MaxPathSize {
124 return nil, errors.New("Too long path")
129 PathLen: uint8(len(pb)),
130 Path: new([MaxPathSize]byte),
132 copy(pkt.Path[:], pb)
136 func blake256() hash.Hash {
137 h, err := blake2b.New256(nil)
144 func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size int64, data io.Reader, out io.Writer) error {
145 pubEph, prvEph, err := box.GenerateKey(rand.Reader)
149 var pktBuf bytes.Buffer
150 if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
159 Size: uint64(size + PktOverhead),
161 var tbsBuf bytes.Buffer
162 if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil {
165 signature := new([ed25519.SignatureSize]byte)
166 copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
175 if _, err = xdr.Marshal(out, &pktEnc); err != nil {
178 sharedKey := new([32]byte)
179 curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
180 kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv1[:])
181 keyEnc := make([]byte, 32)
182 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
185 keyAuth := make([]byte, 64)
186 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
189 ciph, err := twofish.NewCipher(keyEnc)
193 ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
194 mac, err := blake2b.New256(keyAuth)
198 mw := io.MultiWriter(out, mac)
199 ae := &cipher.StreamWriter{S: ctr, W: mw}
200 ae.Write(pktBuf.Bytes())
201 if _, err = io.CopyN(ae, data, int64(size)); err != nil {
205 out.Write(mac.Sum(nil))
209 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
215 ExchPub: pktEnc.ExchPub,
218 var tbsBuf bytes.Buffer
219 if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
222 return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
225 func PktEncRead(our *NodeOur, nodes map[NodeId]*Node, data io.Reader, out io.Writer) (*Node, error) {
227 _, err := xdr.Unmarshal(data, &pktEnc)
231 if pktEnc.Magic != MagicNNCPEv1 {
234 their, known := nodes[*pktEnc.Sender]
236 return nil, errors.New("Unknown sender")
238 verified, err := TbsVerify(our, their, &pktEnc)
243 return their, errors.New("Invalid signature")
245 sharedKey := new([32]byte)
246 curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
247 kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv1[:])
248 keyEnc := make([]byte, 32)
249 if _, err = io.ReadFull(kdf, keyEnc); err != nil {
252 keyAuth := make([]byte, 64)
253 if _, err = io.ReadFull(kdf, keyAuth); err != nil {
256 ciph, err := twofish.NewCipher(keyEnc)
260 ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
261 mac, err := blake2b.New256(keyAuth)
265 trA := io.TeeReader(data, mac)
266 ae := &cipher.StreamReader{S: ctr, R: trA}
267 if _, err = io.CopyN(out, ae, int64(pktEnc.Size)-blake2b.Size256); err != nil {
270 tag := make([]byte, blake2b.Size256)
271 if _, err = io.ReadFull(data, tag); err != nil {
274 if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
275 return their, errors.New("Unauthenticated payload")