/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2020 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2021 Sergey Matveev <stargrave@stargrave.org>
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
"io"
xdr "github.com/davecgh/go-xdr/xdr2"
- "golang.org/x/crypto/blake2b"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/nacl/box"
"golang.org/x/crypto/poly1305"
+ "lukechampine.com/blake3"
)
type PktType uint8
const (
EncBlkSize = 128 * (1 << 10)
- KDFXOFSize = chacha20poly1305.KeySize * 2
- PktTypeFile PktType = iota
- PktTypeFreq PktType = iota
- PktTypeExec PktType = iota
- PktTypeTrns PktType = iota
+ PktTypeFile PktType = iota
+ PktTypeFreq PktType = iota
+ PktTypeExec PktType = iota
+ PktTypeTrns PktType = iota
+ PktTypeExecFat PktType = iota
+ PktTypeArea PktType = iota
MaxPathSize = 1<<8 - 1
NNCPBundlePrefix = "NNCP"
+
+ PktSizeOverhead = 8 + poly1305.TagSize
)
var (
- MagicNNCPPv3 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 3}
- MagicNNCPEv4 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 4}
- BadMagic error = errors.New("Unknown magic number")
- BadPktType error = errors.New("Unknown packet type")
-
- PktOverhead int64
- PktEncOverhead int64
- PktSizeOverhead int64 = 8 + poly1305.TagSize
+ BadPktType error = errors.New("Unknown packet type")
+
+ PktOverhead int64
+ PktEncOverhead int64
)
type Pkt struct {
Type PktType
Nice uint8
PathLen uint8
- Path *[MaxPathSize]byte
+ Path [MaxPathSize]byte
}
type PktTbs struct {
Nice uint8
Sender *NodeId
Recipient *NodeId
- ExchPub *[32]byte
+ ExchPub [32]byte
}
type PktEnc struct {
Nice uint8
Sender *NodeId
Recipient *NodeId
- ExchPub *[32]byte
- Sign *[ed25519.SignatureSize]byte
+ ExchPub [32]byte
+ Sign [ed25519.SignatureSize]byte
}
func init() {
- pkt := Pkt{
- Type: PktTypeFile,
- Path: new([MaxPathSize]byte),
- }
+ pkt := Pkt{Type: PktTypeFile}
var buf bytes.Buffer
n, err := xdr.Marshal(&buf, pkt)
if err != nil {
PktOverhead = int64(n)
buf.Reset()
- dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
+ dummyId, err := NodeIdFromString(DummyB32Id)
if err != nil {
panic(err)
}
pktEnc := PktEnc{
- Magic: MagicNNCPEv4,
- Nice: 123,
+ Magic: MagicNNCPEv5.B,
Sender: dummyId,
Recipient: dummyId,
- ExchPub: new([32]byte),
- Sign: new([ed25519.SignatureSize]byte),
}
n, err = xdr.Marshal(&buf, pktEnc)
if err != nil {
return nil, errors.New("Too long path")
}
pkt := Pkt{
- Magic: MagicNNCPPv3,
+ Magic: MagicNNCPPv3.B,
Type: typ,
Nice: nice,
PathLen: uint8(len(path)),
- Path: new([MaxPathSize]byte),
}
copy(pkt.Path[:], path)
return &pkt, nil
}
+func ctrIncr(b []byte) {
+ for i := len(b) - 1; i >= 0; i-- {
+ b[i]++
+ if b[i] != 0 {
+ return
+ }
+ }
+ panic("counter overflow")
+}
+
func aeadProcess(
aead cipher.AEAD,
- nonce []byte,
+ nonce, ad []byte,
doEncrypt bool,
r io.Reader,
w io.Writer,
) (int, error) {
- var blkCtr uint64
ciphCtr := nonce[len(nonce)-8:]
buf := make([]byte, EncBlkSize+aead.Overhead())
var toRead []byte
}
}
readBytes += n
- blkCtr++
- binary.BigEndian.PutUint64(ciphCtr, blkCtr)
+ ctrIncr(ciphCtr)
if doEncrypt {
- toWrite = aead.Seal(buf[:0], nonce, buf[:n], nil)
+ toWrite = aead.Seal(buf[:0], nonce, buf[:n], ad)
} else {
- toWrite, err = aead.Open(buf[:0], nonce, buf[:n], nil)
+ toWrite, err = aead.Open(buf[:0], nonce, buf[:n], ad)
if err != nil {
return readBytes, err
}
size, padSize int64,
data io.Reader,
out io.Writer,
-) error {
+) ([]byte, error) {
pubEph, prvEph, err := box.GenerateKey(rand.Reader)
if err != nil {
- return err
+ return nil, err
}
var pktBuf bytes.Buffer
if _, err := xdr.Marshal(&pktBuf, pkt); err != nil {
- return err
+ return nil, err
}
tbs := PktTbs{
- Magic: MagicNNCPEv4,
+ Magic: MagicNNCPEv5.B,
Nice: nice,
Sender: our.Id,
Recipient: their.Id,
- ExchPub: pubEph,
+ ExchPub: *pubEph,
}
var tbsBuf bytes.Buffer
if _, err = xdr.Marshal(&tbsBuf, &tbs); err != nil {
- return err
+ return nil, err
}
signature := new([ed25519.SignatureSize]byte)
copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
pktEnc := PktEnc{
- Magic: MagicNNCPEv4,
+ Magic: MagicNNCPEv5.B,
Nice: nice,
Sender: our.Id,
Recipient: their.Id,
- ExchPub: pubEph,
- Sign: signature,
+ ExchPub: *pubEph,
+ Sign: *signature,
+ }
+ ad := blake3.Sum256(tbsBuf.Bytes())
+ tbsBuf.Reset()
+ if _, err = xdr.Marshal(&tbsBuf, &pktEnc); err != nil {
+ return nil, err
}
- if _, err = xdr.Marshal(out, &pktEnc); err != nil {
- return err
+ pktEncRaw := tbsBuf.Bytes()
+ if _, err = out.Write(pktEncRaw); err != nil {
+ return nil, err
}
sharedKey := new([32]byte)
curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
- kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
- if err != nil {
- return err
- }
- if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
- return err
- }
key := make([]byte, chacha20poly1305.KeySize)
- if _, err = io.ReadFull(kdf, key); err != nil {
- return err
- }
+ blake3.DeriveKey(key, string(MagicNNCPEv5.B[:]), sharedKey[:])
aead, err := chacha20poly1305.New(key)
if err != nil {
- return err
+ return nil, err
}
nonce := make([]byte, aead.NonceSize())
fullSize := pktBuf.Len() + int(size)
sizeBuf := make([]byte, 8+aead.Overhead())
binary.BigEndian.PutUint64(sizeBuf, uint64(sizeWithTags(int64(fullSize))))
- if _, err = out.Write(aead.Seal(sizeBuf[:0], nonce, sizeBuf[:8], nil)); err != nil {
- return err
+ if _, err = out.Write(aead.Seal(sizeBuf[:0], nonce, sizeBuf[:8], ad[:])); err != nil {
+ return nil, err
}
lr := io.LimitedReader{R: data, N: size}
mr := io.MultiReader(&pktBuf, &lr)
- written, err := aeadProcess(aead, nonce, true, mr, out)
+ written, err := aeadProcess(aead, nonce, ad[:], true, mr, out)
if err != nil {
- return err
+ return nil, err
}
if written != fullSize {
- return io.ErrUnexpectedEOF
+ return nil, io.ErrUnexpectedEOF
}
if padSize > 0 {
- if _, err = io.ReadFull(kdf, key); err != nil {
- return err
- }
- kdf, err = blake2b.NewXOF(blake2b.OutputLengthUnknown, key)
- if err != nil {
- return err
- }
- if _, err = io.CopyN(out, kdf, padSize); err != nil {
- return err
+ blake3.DeriveKey(key, string(MagicNNCPEv5.B[:])+" PAD", sharedKey[:])
+ xof := blake3.New(32, key).XOF()
+ if _, err = io.CopyN(out, xof, padSize); err != nil {
+ return nil, err
}
}
- return nil
+ return pktEncRaw, nil
}
-func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
+func TbsPrepare(our *NodeOur, their *Node, pktEnc *PktEnc) []byte {
tbs := PktTbs{
- Magic: MagicNNCPEv4,
+ Magic: MagicNNCPEv5.B,
Nice: pktEnc.Nice,
Sender: their.Id,
Recipient: our.Id,
}
var tbsBuf bytes.Buffer
if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
- return false, err
+ panic(err)
}
- return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
+ return tbsBuf.Bytes()
+}
+
+func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) ([]byte, bool, error) {
+ tbs := TbsPrepare(our, their, pktEnc)
+ return tbs, ed25519.Verify(their.SignPub, tbs, pktEnc.Sign[:]), nil
}
func PktEncRead(
nodes map[NodeId]*Node,
data io.Reader,
out io.Writer,
-) (*Node, int64, error) {
+ signatureVerify bool,
+ sharedKeyCached []byte,
+) ([]byte, *Node, int64, error) {
var pktEnc PktEnc
_, err := xdr.Unmarshal(data, &pktEnc)
if err != nil {
- return nil, 0, err
- }
- if pktEnc.Magic != MagicNNCPEv4 {
- return nil, 0, BadMagic
- }
- their, known := nodes[*pktEnc.Sender]
- if !known {
- return nil, 0, errors.New("Unknown sender")
+ return nil, nil, 0, err
+ }
+ switch pktEnc.Magic {
+ case MagicNNCPEv1.B:
+ err = MagicNNCPEv1.TooOld()
+ case MagicNNCPEv2.B:
+ err = MagicNNCPEv2.TooOld()
+ case MagicNNCPEv3.B:
+ err = MagicNNCPEv3.TooOld()
+ case MagicNNCPEv4.B:
+ err = MagicNNCPEv4.TooOld()
+ case MagicNNCPEv5.B:
+ default:
+ err = BadMagic
}
- if *pktEnc.Recipient != *our.Id {
- return nil, 0, errors.New("Invalid recipient")
- }
- verified, err := TbsVerify(our, their, &pktEnc)
if err != nil {
- return nil, 0, err
+ return nil, nil, 0, err
}
- if !verified {
- return their, 0, errors.New("Invalid signature")
+ if *pktEnc.Recipient != *our.Id {
+ return nil, nil, 0, errors.New("Invalid recipient")
+ }
+ var tbsRaw []byte
+ var their *Node
+ if signatureVerify {
+ their = nodes[*pktEnc.Sender]
+ if their == nil {
+ return nil, nil, 0, errors.New("Unknown sender")
+ }
+ var verified bool
+ tbsRaw, verified, err = TbsVerify(our, their, &pktEnc)
+ if err != nil {
+ return nil, nil, 0, err
+ }
+ if !verified {
+ return nil, their, 0, errors.New("Invalid signature")
+ }
+ } else {
+ tbsRaw = TbsPrepare(our, &Node{Id: pktEnc.Sender}, &pktEnc)
}
+ ad := blake3.Sum256(tbsRaw)
sharedKey := new([32]byte)
- curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
- kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:])
- if err != nil {
- return their, 0, err
- }
- if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
- return their, 0, err
+ if sharedKeyCached == nil {
+ curve25519.ScalarMult(sharedKey, our.ExchPrv, &pktEnc.ExchPub)
+ } else {
+ copy(sharedKey[:], sharedKeyCached)
}
key := make([]byte, chacha20poly1305.KeySize)
- if _, err = io.ReadFull(kdf, key); err != nil {
- return their, 0, err
- }
+ blake3.DeriveKey(key, string(MagicNNCPEv5.B[:]), sharedKey[:])
aead, err := chacha20poly1305.New(key)
if err != nil {
- return their, 0, err
+ return sharedKey[:], their, 0, err
}
nonce := make([]byte, aead.NonceSize())
sizeBuf := make([]byte, 8+aead.Overhead())
if _, err = io.ReadFull(data, sizeBuf); err != nil {
- return their, 0, err
+ return sharedKey[:], their, 0, err
}
- sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, nil)
+ sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, ad[:])
if err != nil {
- return their, 0, err
+ return sharedKey[:], their, 0, err
}
size := int64(binary.BigEndian.Uint64(sizeBuf))
lr := io.LimitedReader{R: data, N: size}
- written, err := aeadProcess(aead, nonce, false, &lr, out)
+ written, err := aeadProcess(aead, nonce, ad[:], false, &lr, out)
if err != nil {
- return their, int64(written), err
+ return sharedKey[:], their, int64(written), err
}
if written != int(size) {
- return their, int64(written), io.ErrUnexpectedEOF
+ return sharedKey[:], their, int64(written), io.ErrUnexpectedEOF
}
- return their, size, nil
+ return sharedKey[:], their, size, nil
}