]> Cypherpunks.ru repositories - nncp.git/commitdiff
AD for AEAD
authorSergey Matveev <stargrave@stargrave.org>
Tue, 29 Jun 2021 11:05:15 +0000 (14:05 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Wed, 30 Jun 2021 10:56:52 +0000 (13:56 +0300)
doc/news.ru.texi
doc/news.texi
doc/pkt.texi
src/pkt.go
src/tx.go

index 7046d997a93882264167365ff5c47f769f5c23df..467d5d0c808e0f5f81b381cea87ad501d70799da 100644 (file)
 @item
 Добавлена @command{nncp-hash} утилита для вычисления MTH хэша файла.
 
+@item
+В шифрованных пакетах BLAKE2 KDF и XOF функции заменены на BLAKE3. Ещё
+уменьшая количество примитивов. А также заголовок шифрованного файла
+теперь является ассоциированными данными при шифровании.
+
 @item
 MultiCast Discovery использует
 @verb{|ff02::4e4e:4350|} адрес вместо @verb{|ff02::1|}.
index 7314d4efeb44e48f25b38c483ec32862f93995c4..e06cae8e52d149b7dd8091a7df728da2106333be 100644 (file)
@@ -26,6 +26,11 @@ preceding part of file, not the whole one as was before.
 @item
 @command{nncp-hash} utility appeared for calculating file's MTH hash.
 
+@item
+BLAKE2 KDF and XOF functions are replaced with BLAKE3 in encrypted
+packets. Lowering number of used primitives. Also, its encrypted
+packet's header is used as an associated data during encryption.
+
 @item
 MultiCast Discovery uses
 @verb{|ff02::4e4e:4350|} address instead of @verb{|ff02::1|}.
index 567d3f77226d7e2d192de79b3ea5fd14ddf8d744..b47911330cd05978912fa2e317a30a995076de8c 100644 (file)
@@ -110,26 +110,20 @@ Each encrypted packet has the following header:
     Ephemeral curve25519 public key
 @item Signature @tab
     64-byte, fixed length opaque data @tab
-    ed25519 signature for that packet's header
+    ed25519 signature for that packet's header over all previous fields.
 @end multitable
 
-Signature is calculated over all previous fields.
-
 All following encryption is done in AEAD mode using
 @url{https://cr.yp.to/chacha.html, ChaCha20}-@url{https://en.wikipedia.org/wiki/Poly1305, Poly1305}
-algorithms. Data is divided on 128 KiB blocks. Each block is encrypted with
-increasing nonce counter.
-
-Authenticated and encrypted size come after the header:
-
-@multitable @columnfractions 0.2 0.3 0.5
-@headitem @tab XDR type @tab Value
-@item Size @tab
-    unsigned hyper integer @tab
-    Payload size.
-@end multitable
+algorithms. Authenticated data is BLAKE3-256 hash of the unsigned
+portion of the header (the same data used in the signature). Size is
+XDR-encoded unsigned hyper integer, carrying the payload size, encrypted
+as a single AEAD-block (with the tag) independently from the following
+blocks. It is encoded with the zero nonce.
 
-Then comes the actual payload.
+Payload with possible padding is divided on 128 KiB blocks blocks. They
+are encrypted with the same authenticated data and increasing big-endian
+64-bit nonce, starting at 1.
 
 Each node has static @strong{exchange} and @strong{signature} keypairs.
 When node A want to send encrypted packet to node B, it:
@@ -145,8 +139,12 @@ When node A want to send encrypted packet to node B, it:
 @item derives 32-bytes AEAD encryption key with BLAKE3 derivation
     function. Source key is the derived ephemeral key. Context is
     @verb{|N N C P E 0x00 0x00 0x05|} magic number
+@item calculates authenticated data: it is BLAKE3-256 hash of the
+    unsigned header (same used for signing)
 @item encrypts size, appends its authenticated ciphertext to the header
+    (with authenticated data, nonce=0)
 @item encrypts each payload block, appending its authenticated ciphertext
+    (with authenticated data, nonce starting at 1, increasing with each block)
 @item possibly appends any kind of "junk" noise data to hide real
     payload's size from the adversary (generated using BLAKE3 XOF, with
     the key derived from the ephemeral one and context string of
index 6bc9c919e873fecba3706fc3eba711a0fdca7aa0..3aed5a64c30674d7ad1c3e1b24377604325442c1 100644 (file)
@@ -139,7 +139,7 @@ func ctrIncr(b []byte) {
 
 func aeadProcess(
        aead cipher.AEAD,
-       nonce []byte,
+       nonce, ad []byte,
        doEncrypt bool,
        r io.Reader,
        w io.Writer,
@@ -169,9 +169,9 @@ func aeadProcess(
                readBytes += n
                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
                        }
@@ -229,6 +229,7 @@ func PktEncWrite(
                ExchPub:   *pubEph,
                Sign:      *signature,
        }
+       ad := blake3.Sum256(tbsBuf.Bytes())
        tbsBuf.Reset()
        if _, err = xdr.Marshal(&tbsBuf, &pktEnc); err != nil {
                return nil, err
@@ -251,13 +252,13 @@ func PktEncWrite(
        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 {
+       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 nil, err
        }
@@ -274,7 +275,7 @@ func PktEncWrite(
        return pktEncRaw, nil
 }
 
-func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
+func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) ([]byte, bool, error) {
        tbs := PktTbs{
                Magic:     MagicNNCPEv5,
                Nice:      pktEnc.Nice,
@@ -284,9 +285,9 @@ func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
        }
        var tbsBuf bytes.Buffer
        if _, err := xdr.Marshal(&tbsBuf, &tbs); err != nil {
-               return false, err
+               return nil, false, err
        }
-       return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
+       return tbsBuf.Bytes(), ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
 }
 
 func PktEncRead(
@@ -310,13 +311,14 @@ func PktEncRead(
        if *pktEnc.Recipient != *our.Id {
                return nil, 0, errors.New("Invalid recipient")
        }
-       verified, err := TbsVerify(our, their, &pktEnc)
+       tbsRaw, verified, err := TbsVerify(our, their, &pktEnc)
        if err != nil {
                return nil, 0, err
        }
        if !verified {
                return their, 0, errors.New("Invalid signature")
        }
+       ad := blake3.Sum256(tbsRaw)
        sharedKey := new([32]byte)
        curve25519.ScalarMult(sharedKey, our.ExchPrv, &pktEnc.ExchPub)
 
@@ -332,14 +334,14 @@ func PktEncRead(
        if _, err = io.ReadFull(data, sizeBuf); err != nil {
                return 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
        }
        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
        }
index 20482efb234bc5a99e4b2fc5b792d7a976ff8c3a..f142d2f9205f84e0f40f2b931caac1bb1d7c329f 100644 (file)
--- a/src/tx.go
+++ b/src/tx.go
@@ -185,7 +185,7 @@ func throughTmpFile(r io.Reader) (
                return
        }
        nonce := make([]byte, aead.NonceSize())
-       written, err := aeadProcess(aead, nonce, true, r, tmpW)
+       written, err := aeadProcess(aead, nonce, nil, true, r, tmpW)
        if err != nil {
                rerr = err
                return
@@ -201,7 +201,7 @@ func throughTmpFile(r io.Reader) (
        }
        r, w := io.Pipe()
        go func() {
-               if _, err := aeadProcess(aead, nonce, false, bufio.NewReader(src), w); err != nil {
+               if _, err := aeadProcess(aead, nonce, nil, false, bufio.NewReader(src), w); err != nil {
                        w.CloseWithError(err) // #nosec G104
                }
        }()