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
MaxPathSize = 1<<8 - 1
DefaultNiceMail = 64
- DefaultNiceFreq = 196
+ DefaultNiceFreq = 64
DefaultNiceFile = 196
+
+ NNCPBundlePrefix = "NNCP"
)
var (
- MagicNNCPPv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 1, 0, 0}
- MagicNNCPEv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 1, 0, 0}
+ MagicNNCPPv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 1}
+ 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")
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() {
if err != nil {
panic(err)
}
- PktOverhead = int64(n) + blake2b.Size256
+ PktOverhead = 8 + blake2b.Size256 + int64(n) + blake2b.Size256
buf.Reset()
dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
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 {
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 PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size int64, data io.Reader, out io.Writer) error {
+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
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 {
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
+ }
+ 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 {
+ 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 {
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
+ return their, 0, err
}
- ciph, err := twofish.NewCipher(keyEnc)
- if err != nil {
- return their, err
- }
- ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
mac, err := blake2b.New256(keyAuth)
if err != nil {
- return their, err
+ 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
+
+ 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 _, 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
}