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
var (
MagicNNCPPv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 1}
- MagicNNCPEv2 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 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")
panic(err)
}
pktEnc := PktEnc{
- Magic: MagicNNCPEv2,
+ Magic: MagicNNCPEv3,
Nice: 123,
Sender: dummyId,
Recipient: dummyId,
return &pkt, nil
}
-func blake256() hash.Hash {
- h, err := blake2b.New256(nil)
- if err != nil {
- panic(err)
- }
- return h
-}
-
type DevZero struct{}
func (d DevZero) Read(b []byte) (n int, err error) {
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, padSize int64, data io.Reader, out io.Writer) error {
pubEph, prvEph, err := box.GenerateKey(rand.Reader)
if err != nil {
return err
}
tbs := PktTbs{
- Magic: MagicNNCPEv2,
+ Magic: MagicNNCPEv3,
Nice: nice,
Sender: our.Id,
Recipient: their.Id,
signature := new([ed25519.SignatureSize]byte)
copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
pktEnc := PktEnc{
- Magic: MagicNNCPEv2,
+ Magic: MagicNNCPEv3,
Nice: nice,
Sender: our.Id,
Recipient: their.Id,
}
sharedKey := new([32]byte)
curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
- kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv2[:])
-
- keyEnc := make([]byte, 32)
- if _, err = io.ReadFull(kdf, keyEnc); err != nil {
+ kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
+ if err != nil {
return err
}
- keyAuth := make([]byte, 64)
- if _, err = io.ReadFull(kdf, keyAuth); err != nil {
+ if _, err = kdf.Write(MagicNNCPEv3[:]); err != nil {
return err
}
- ciph, err := twofish.NewCipher(keyEnc)
- if err != nil {
+ 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
}
- ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
mac, err := blake2b.New256(keyAuth)
if err != nil {
return err
}
- mw := io.MultiWriter(out, mac)
- ae := &cipher.StreamWriter{S: ctr, W: mw}
- usize := uint64(size)
- if _, err = xdr.Marshal(ae, &usize); err != nil {
+ 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
}
- ae.Close()
- out.Write(mac.Sum(nil))
- if _, err = io.ReadFull(kdf, keyEnc); err != nil {
+ if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
return err
}
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)
+ lr := io.LimitedReader{data, size}
+ mr := io.MultiReader(&pktBuf, &lr)
+ mw := io.MultiWriter(out, mac)
+ fullSize := pktBuf.Len() + int(size)
+ written, err := ae(keyEnc, mr, mw)
if err != nil {
return err
}
-
- mw = io.MultiWriter(out, mac)
- ae = &cipher.StreamWriter{S: ctr, W: mw}
- ae.Write(pktBuf.Bytes())
- if _, err = io.CopyN(ae, data, size); err != nil {
+ if written != fullSize {
+ return io.ErrUnexpectedEOF
+ }
+ if _, err = out.Write(mac.Sum(nil)); err != nil {
return err
}
- ae.Close()
- out.Write(mac.Sum(nil))
-
if padSize > 0 {
- if _, err = io.ReadFull(kdf, keyEnc); err != nil {
+ if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
return err
}
- ciph, err = twofish.NewCipher(keyEnc)
+ lr = io.LimitedReader{DevZero{}, padSize}
+ written, err = ae(keyEnc, &lr, out)
if err != nil {
return err
}
- ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
- ae = &cipher.StreamWriter{S: ctr, W: out}
- if _, err = io.CopyN(ae, DevZero{}, padSize); err != nil {
- return err
+ if written != int(padSize) {
+ return io.ErrUnexpectedEOF
}
- ae.Close()
}
return nil
}
func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
tbs := PktTbs{
- Magic: MagicNNCPEv2,
+ Magic: MagicNNCPEv3,
Nice: pktEnc.Nice,
Sender: their.Id,
Recipient: our.Id,
if err != nil {
return nil, 0, err
}
- if pktEnc.Magic != MagicNNCPEv2 {
+ if pktEnc.Magic != MagicNNCPEv3 {
return nil, 0, BadMagic
}
their, known := nodes[*pktEnc.Sender]
}
sharedKey := new([32]byte)
curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
- kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv2[:])
-
- keyEnc := make([]byte, 32)
- if _, err = io.ReadFull(kdf, keyEnc); err != nil {
+ kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
+ if err != nil {
return their, 0, err
}
- keyAuth := make([]byte, 64)
- if _, err = io.ReadFull(kdf, keyAuth); err != nil {
+ if _, err = kdf.Write(MagicNNCPEv3[:]); err != nil {
return their, 0, err
}
- ciph, err := twofish.NewCipher(keyEnc)
- if err != nil {
+ 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, 0, err
}
- ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
mac, err := blake2b.New256(keyAuth)
if err != nil {
return their, 0, err
}
- tr := io.TeeReader(data, mac)
- ae := &cipher.StreamReader{S: ctr, R: tr}
- var usize uint64
- if _, err = xdr.Unmarshal(ae, &usize); err != nil {
+ sizeBuf := make([]byte, 8)
+ if _, err = io.ReadFull(data, sizeBuf); err != nil {
+ return their, 0, err
+ }
+ if _, err = mac.Write(sizeBuf); err != nil {
return their, 0, err
}
tag := make([]byte, blake2b.Size256)
if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
return their, 0, errors.New("Unauthenticated size")
}
- size := int64(usize)
+ chacha20.XORKeyStream(sizeBuf, sizeBuf, new([16]byte), keyEnc)
+ size := int64(binary.BigEndian.Uint64(sizeBuf))
- if _, err = io.ReadFull(kdf, keyEnc); err != nil {
+ if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
return their, size, err
}
if _, err = io.ReadFull(kdf, keyAuth); err != nil {
return their, size, err
}
-
- ciph, err = twofish.NewCipher(keyEnc)
- if err != nil {
- return their, size, err
- }
- ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
mac, err = blake2b.New256(keyAuth)
if err != nil {
- return their, size, err
+ return their, 0, err
}
- tr = io.TeeReader(data, mac)
- ae = &cipher.StreamReader{S: ctr, R: tr}
- if _, err = io.CopyN(out, ae, PktOverhead+size-8-blake2b.Size256-blake2b.Size256); err != nil {
- return their, size, 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