2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2021 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, version 3 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 xdr "github.com/davecgh/go-xdr/xdr2"
29 "golang.org/x/crypto/chacha20poly1305"
30 "golang.org/x/crypto/curve25519"
31 "golang.org/x/crypto/ed25519"
32 "golang.org/x/crypto/nacl/box"
33 "golang.org/x/crypto/poly1305"
34 "lukechampine.com/blake3"
40 EncBlkSize = 128 * (1 << 10)
42 PktTypeFile PktType = iota
43 PktTypeFreq PktType = iota
44 PktTypeExec PktType = iota
45 PktTypeTrns PktType = iota
46 PktTypeExecFat PktType = iota
48 MaxPathSize = 1<<8 - 1
50 NNCPBundlePrefix = "NNCP"
52 PktSizeOverhead = 8 + poly1305.TagSize
56 BadPktType error = errors.New("Unknown packet type")
67 Path [MaxPathSize]byte
84 Sign [ed25519.SignatureSize]byte
88 pkt := Pkt{Type: PktTypeFile}
90 n, err := xdr.Marshal(&buf, pkt)
94 PktOverhead = int64(n)
97 dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
102 Magic: MagicNNCPEv5.B,
106 n, err = xdr.Marshal(&buf, pktEnc)
110 PktEncOverhead = int64(n)
113 func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
114 if len(path) > MaxPathSize {
115 return nil, errors.New("Too long path")
118 Magic: MagicNNCPPv3.B,
121 PathLen: uint8(len(path)),
123 copy(pkt.Path[:], path)
127 func ctrIncr(b []byte) {
128 for i := len(b) - 1; i >= 0; i-- {
134 panic("counter overflow")
144 ciphCtr := nonce[len(nonce)-8:]
145 buf := make([]byte, EncBlkSize+aead.Overhead())
152 toRead = buf[:EncBlkSize]
157 n, err = io.ReadFull(r, toRead)
162 if err != io.ErrUnexpectedEOF {
163 return readBytes + n, err
169 toWrite = aead.Seal(buf[:0], nonce, buf[:n], ad)
171 toWrite, err = aead.Open(buf[:0], nonce, buf[:n], ad)
173 return readBytes, err
176 if _, err = w.Write(toWrite); err != nil {
177 return readBytes, err
180 return readBytes, nil
183 func sizeWithTags(size int64) (fullSize int64) {
184 fullSize = size + (size/EncBlkSize)*poly1305.TagSize
185 if size%EncBlkSize != 0 {
186 fullSize += poly1305.TagSize
200 pubEph, prvEph, err := box.GenerateKey(rand.Reader)
204 var pktBuf bytes.Buffer
205 if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
209 Magic: MagicNNCPEv5.B,
215 var tbsBuf bytes.Buffer
216 if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil {
219 signature := new([ed25519.SignatureSize]byte)
220 copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
222 Magic: MagicNNCPEv5.B,
229 ad := blake3.Sum256(tbsBuf.Bytes())
231 if _, err = xdr.Marshal(&tbsBuf, &pktEnc); err != nil {
234 pktEncRaw := tbsBuf.Bytes()
235 if _, err = out.Write(pktEncRaw); err != nil {
238 sharedKey := new([32]byte)
239 curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
241 key := make([]byte, chacha20poly1305.KeySize)
242 blake3.DeriveKey(key, string(MagicNNCPEv5.B[:]), sharedKey[:])
243 aead, err := chacha20poly1305.New(key)
247 nonce := make([]byte, aead.NonceSize())
249 fullSize := pktBuf.Len() + int(size)
250 sizeBuf := make([]byte, 8+aead.Overhead())
251 binary.BigEndian.PutUint64(sizeBuf, uint64(sizeWithTags(int64(fullSize))))
252 if _, err = out.Write(aead.Seal(sizeBuf[:0], nonce, sizeBuf[:8], ad[:])); err != nil {
256 lr := io.LimitedReader{R: data, N: size}
257 mr := io.MultiReader(&pktBuf, &lr)
258 written, err := aeadProcess(aead, nonce, ad[:], true, mr, out)
262 if written != fullSize {
263 return nil, io.ErrUnexpectedEOF
266 blake3.DeriveKey(key, string(MagicNNCPEv5.B[:])+" PAD", sharedKey[:])
267 xof := blake3.New(32, key).XOF()
268 if _, err = io.CopyN(out, xof, padSize); err != nil {
272 return pktEncRaw, nil
275 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) ([]byte, bool, error) {
277 Magic: MagicNNCPEv5.B,
281 ExchPub: pktEnc.ExchPub,
283 var tbsBuf bytes.Buffer
284 if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
285 return nil, false, err
287 return tbsBuf.Bytes(), ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
292 nodes map[NodeId]*Node,
295 ) (*Node, int64, error) {
297 _, err := xdr.Unmarshal(data, &pktEnc)
301 switch pktEnc.Magic {
303 err = MagicNNCPEv1.TooOld()
305 err = MagicNNCPEv2.TooOld()
307 err = MagicNNCPEv3.TooOld()
309 err = MagicNNCPEv4.TooOld()
317 their, known := nodes[*pktEnc.Sender]
319 return nil, 0, errors.New("Unknown sender")
321 if *pktEnc.Recipient != *our.Id {
322 return nil, 0, errors.New("Invalid recipient")
324 tbsRaw, verified, err := TbsVerify(our, their, &pktEnc)
329 return their, 0, errors.New("Invalid signature")
331 ad := blake3.Sum256(tbsRaw)
332 sharedKey := new([32]byte)
333 curve25519.ScalarMult(sharedKey, our.ExchPrv, &pktEnc.ExchPub)
335 key := make([]byte, chacha20poly1305.KeySize)
336 blake3.DeriveKey(key, string(MagicNNCPEv5.B[:]), sharedKey[:])
337 aead, err := chacha20poly1305.New(key)
341 nonce := make([]byte, aead.NonceSize())
343 sizeBuf := make([]byte, 8+aead.Overhead())
344 if _, err = io.ReadFull(data, sizeBuf); err != nil {
347 sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, ad[:])
351 size := int64(binary.BigEndian.Uint64(sizeBuf))
353 lr := io.LimitedReader{R: data, N: size}
354 written, err := aeadProcess(aead, nonce, ad[:], false, &lr, out)
356 return their, int64(written), err
358 if written != int(size) {
359 return their, int64(written), io.ErrUnexpectedEOF
361 return their, size, nil