X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=src%2Fcypherpunks.ru%2Fnncp%2Fpkt.go;h=b1c506a590d4ec816a61ac030781aa2645a7a8ea;hb=a4262555b0506c1cf85b9d5d4dae58bf3922e629;hp=2f240341c203810eccd3fe20e589a59bbb0f4b97;hpb=adae7497b417f104534ce3d097638e9f76007191;p=nncp.git diff --git a/src/cypherpunks.ru/nncp/pkt.go b/src/cypherpunks.ru/nncp/pkt.go index 2f24034..b1c506a 100644 --- a/src/cypherpunks.ru/nncp/pkt.go +++ b/src/cypherpunks.ru/nncp/pkt.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2017 Sergey Matveev +Copyright (C) 2016-2018 Sergey Matveev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,40 +20,43 @@ package nncp import ( "bytes" - "crypto/cipher" "crypto/rand" "crypto/subtle" + "encoding/binary" "errors" - "hash" "io" + "chacha20" "github.com/davecgh/go-xdr/xdr2" "golang.org/x/crypto/blake2b" "golang.org/x/crypto/curve25519" "golang.org/x/crypto/ed25519" - "golang.org/x/crypto/hkdf" "golang.org/x/crypto/nacl/box" - "golang.org/x/crypto/twofish" ) type PktType uint8 const ( + EncBlkSize = 128 * (1 << 10) + KDFXOFSize = 2*(32+64) + 32 + PktTypeFile PktType = iota PktTypeFreq PktType = iota - PktTypeMail PktType = iota + PktTypeExec PktType = iota PktTypeTrns PktType = iota MaxPathSize = 1<<8 - 1 - DefaultNiceMail = 64 - DefaultNiceFreq = 196 + DefaultNiceExec = 64 + DefaultNiceFreq = 64 DefaultNiceFile = 196 + + NNCPBundlePrefix = "NNCP" ) var ( - MagicNNCPPv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 1} - MagicNNCPEv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 1} + MagicNNCPPv2 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 2} + MagicNNCPEv3 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 3} BadMagic error = errors.New("Unknown magic number") BadPktType error = errors.New("Unknown packet type") @@ -64,6 +67,7 @@ var ( type Pkt struct { Magic [8]byte Type PktType + Nice uint8 PathLen uint8 Path *[MaxPathSize]byte } @@ -71,19 +75,18 @@ type Pkt struct { type PktTbs struct { Magic [8]byte Nice uint8 - Recipient *NodeId Sender *NodeId + Recipient *NodeId ExchPub *[32]byte - Size uint64 } type PktEnc struct { - Magic [8]byte - Nice uint8 - Sender *NodeId - ExchPub *[32]byte - Sign *[ed25519.SignatureSize]byte - Size uint64 + Magic [8]byte + Nice uint8 + Sender *NodeId + Recipient *NodeId + ExchPub *[32]byte + Sign *[ed25519.SignatureSize]byte } func init() { @@ -96,7 +99,7 @@ func init() { if err != nil { panic(err) } - PktOverhead = int64(n) + blake2b.Size256 + PktOverhead = 8 + blake2b.Size256 + int64(n) + blake2b.Size256 buf.Reset() dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") @@ -104,12 +107,12 @@ func init() { panic(err) } pktEnc := PktEnc{ - Magic: MagicNNCPEv1, - Nice: 123, - Sender: dummyId, - ExchPub: new([32]byte), - Sign: new([ed25519.SignatureSize]byte), - Size: 123, + Magic: MagicNNCPEv3, + Nice: 123, + Sender: dummyId, + Recipient: dummyId, + ExchPub: new([32]byte), + Sign: new([ed25519.SignatureSize]byte), } n, err = xdr.Marshal(&buf, pktEnc) if err != nil { @@ -118,30 +121,60 @@ func init() { PktEncOverhead = int64(n) } -func NewPkt(typ PktType, path string) (*Pkt, error) { - pb := []byte(path) - if len(pb) > MaxPathSize { +func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) { + if len(path) > MaxPathSize { return nil, errors.New("Too long path") } pkt := Pkt{ - Magic: MagicNNCPPv1, + Magic: MagicNNCPPv2, Type: typ, - PathLen: uint8(len(pb)), + Nice: nice, + PathLen: uint8(len(path)), Path: new([MaxPathSize]byte), } - copy(pkt.Path[:], pb) + copy(pkt.Path[:], path) return &pkt, nil } -func blake256() hash.Hash { - h, err := blake2b.New256(nil) - if err != nil { - panic(err) +type DevZero struct{} + +func (d DevZero) Read(b []byte) (n int, err error) { + for n = 0; n < len(b); n++ { + b[n] = 0 } - return h + return +} + +func ae(keyEnc *[32]byte, r io.Reader, w io.Writer) (int, error) { + var blkCtr uint64 + ciphNonce := new([16]byte) + ciphCtr := ciphNonce[8:] + buf := make([]byte, EncBlkSize) + var n int + var written int + var err error + for { + n, err = io.ReadFull(r, buf) + if err != nil { + if err == io.EOF { + break + } + if err != io.ErrUnexpectedEOF { + return written + n, err + } + } + written += n + blkCtr++ + binary.BigEndian.PutUint64(ciphCtr, blkCtr) + chacha20.XORKeyStream(buf[:n], buf[:n], ciphNonce, keyEnc) + if _, err = w.Write(buf[:n]); err != nil { + return written, err + } + } + return written, nil } -func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size int64, data io.Reader, out io.Writer) error { +func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size, padSize int64, data io.Reader, out io.Writer) error { pubEph, prvEph, err := box.GenerateKey(rand.Reader) if err != nil { return err @@ -151,12 +184,11 @@ func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size int64, da return err } tbs := PktTbs{ - Magic: MagicNNCPEv1, + Magic: MagicNNCPEv3, Nice: nice, - Recipient: their.Id, Sender: our.Id, + Recipient: their.Id, ExchPub: pubEph, - Size: uint64(size + PktOverhead), } var tbsBuf bytes.Buffer if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil { @@ -165,55 +197,99 @@ func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size int64, da signature := new([ed25519.SignatureSize]byte) copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes())) pktEnc := PktEnc{ - Magic: MagicNNCPEv1, - Nice: nice, - Sender: our.Id, - ExchPub: pubEph, - Sign: signature, - Size: tbs.Size, + Magic: MagicNNCPEv3, + Nice: nice, + Sender: our.Id, + Recipient: their.Id, + ExchPub: pubEph, + Sign: signature, } if _, err = xdr.Marshal(out, &pktEnc); err != nil { return err } sharedKey := new([32]byte) curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub) - kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv1[:]) - keyEnc := make([]byte, 32) - if _, err = io.ReadFull(kdf, keyEnc); err != nil { + kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:]) + if err != nil { + return err + } + if _, err = kdf.Write(MagicNNCPEv3[:]); err != nil { + return err + } + + keyEnc := new([32]byte) + if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil { return err } keyAuth := make([]byte, 64) if _, err = io.ReadFull(kdf, keyAuth); err != nil { return err } - ciph, err := twofish.NewCipher(keyEnc) + mac, err := blake2b.New256(keyAuth) if err != nil { return err } - ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize)) - mac, err := blake2b.New256(keyAuth) + + sizeBuf := make([]byte, 8) + binary.BigEndian.PutUint64(sizeBuf, uint64(size)) + chacha20.XORKeyStream(sizeBuf, sizeBuf, new([16]byte), keyEnc) + if _, err = out.Write(sizeBuf); err != nil { + return err + } + if _, err = mac.Write(sizeBuf); err != nil { + return err + } + if _, err = out.Write(mac.Sum(nil)); err != nil { + return err + } + + if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil { + return err + } + if _, err = io.ReadFull(kdf, keyAuth); err != nil { + return err + } + mac, err = blake2b.New256(keyAuth) if err != nil { return err } + lr := io.LimitedReader{data, size} + mr := io.MultiReader(&pktBuf, &lr) mw := io.MultiWriter(out, mac) - ae := &cipher.StreamWriter{S: ctr, W: mw} - ae.Write(pktBuf.Bytes()) - if _, err = io.CopyN(ae, data, int64(size)); err != nil { + fullSize := pktBuf.Len() + int(size) + written, err := ae(keyEnc, mr, mw) + if err != nil { return err } - ae.Close() - out.Write(mac.Sum(nil)) + if written != fullSize { + return io.ErrUnexpectedEOF + } + if _, err = out.Write(mac.Sum(nil)); err != nil { + return err + } + if padSize > 0 { + if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil { + return err + } + lr = io.LimitedReader{DevZero{}, padSize} + written, err = ae(keyEnc, &lr, out) + if err != nil { + return err + } + if written != int(padSize) { + return io.ErrUnexpectedEOF + } + } return nil } func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) { tbs := PktTbs{ - Magic: MagicNNCPEv1, + Magic: MagicNNCPEv3, Nice: pktEnc.Nice, - Recipient: our.Id, Sender: their.Id, + Recipient: our.Id, ExchPub: pktEnc.ExchPub, - Size: pktEnc.Size, } var tbsBuf bytes.Buffer if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil { @@ -222,57 +298,95 @@ func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) { return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil } -func PktEncRead(our *NodeOur, nodes map[NodeId]*Node, data io.Reader, out io.Writer) (*Node, error) { +func PktEncRead(our *NodeOur, nodes map[NodeId]*Node, data io.Reader, out io.Writer) (*Node, int64, error) { var pktEnc PktEnc _, err := xdr.Unmarshal(data, &pktEnc) if err != nil { - return nil, err + return nil, 0, err } - if pktEnc.Magic != MagicNNCPEv1 { - return nil, BadMagic + if pktEnc.Magic != MagicNNCPEv3 { + return nil, 0, BadMagic } their, known := nodes[*pktEnc.Sender] if !known { - return nil, errors.New("Unknown sender") + return nil, 0, errors.New("Unknown sender") + } + if *pktEnc.Recipient != *our.Id { + return nil, 0, errors.New("Invalid recipient") } verified, err := TbsVerify(our, their, &pktEnc) if err != nil { - return nil, err + return nil, 0, err } if !verified { - return their, errors.New("Invalid signature") + return their, 0, errors.New("Invalid signature") } sharedKey := new([32]byte) curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub) - kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv1[:]) - keyEnc := make([]byte, 32) - if _, err = io.ReadFull(kdf, keyEnc); err != nil { - return their, err + kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:]) + if err != nil { + return their, 0, err + } + if _, err = kdf.Write(MagicNNCPEv3[:]); err != nil { + return their, 0, err + } + + keyEnc := new([32]byte) + if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil { + return their, 0, err } keyAuth := make([]byte, 64) if _, err = io.ReadFull(kdf, keyAuth); err != nil { - return their, err - } - ciph, err := twofish.NewCipher(keyEnc) - if err != nil { - return their, err + return their, 0, err } - ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize)) mac, err := blake2b.New256(keyAuth) if err != nil { - return their, err + return their, 0, err + } + + sizeBuf := make([]byte, 8) + if _, err = io.ReadFull(data, sizeBuf); err != nil { + return their, 0, err } - trA := io.TeeReader(data, mac) - ae := &cipher.StreamReader{S: ctr, R: trA} - if _, err = io.CopyN(out, ae, int64(pktEnc.Size)-blake2b.Size256); err != nil { - return their, err + if _, err = mac.Write(sizeBuf); err != nil { + return their, 0, err } tag := make([]byte, blake2b.Size256) if _, err = io.ReadFull(data, tag); err != nil { - return their, err + return their, 0, err + } + if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 { + return their, 0, errors.New("Unauthenticated size") + } + chacha20.XORKeyStream(sizeBuf, sizeBuf, new([16]byte), keyEnc) + size := int64(binary.BigEndian.Uint64(sizeBuf)) + + if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil { + return their, size, err + } + if _, err = io.ReadFull(kdf, keyAuth); err != nil { + return their, size, err + } + mac, err = blake2b.New256(keyAuth) + if err != nil { + return their, 0, err + } + + fullSize := PktOverhead + size - 8 - 2*blake2b.Size256 + lr := io.LimitedReader{data, fullSize} + tr := io.TeeReader(&lr, mac) + written, err := ae(keyEnc, tr, out) + if err != nil { + return their, int64(written), err + } + if written != int(fullSize) { + return their, int64(written), io.ErrUnexpectedEOF + } + if _, err = io.ReadFull(data, tag); err != nil { + return their, size, err } if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 { - return their, errors.New("Unauthenticated payload") + return their, size, errors.New("Unauthenticated payload") } - return their, nil + return their, size, nil }