From 73528720c617ae7915484929676419220f61736e Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Tue, 29 Jun 2021 14:05:15 +0300 Subject: [PATCH] AD for AEAD --- doc/news.ru.texi | 5 +++++ doc/news.texi | 5 +++++ doc/pkt.texi | 28 +++++++++++++--------------- src/pkt.go | 24 +++++++++++++----------- src/tx.go | 4 ++-- 5 files changed, 38 insertions(+), 28 deletions(-) diff --git a/doc/news.ru.texi b/doc/news.ru.texi index 7046d99..467d5d0 100644 --- a/doc/news.ru.texi +++ b/doc/news.ru.texi @@ -25,6 +25,11 @@ @item Добавлена @command{nncp-hash} утилита для вычисления MTH хэша файла. +@item +В шифрованных пакетах BLAKE2 KDF и XOF функции заменены на BLAKE3. Ещё +уменьшая количество примитивов. А также заголовок шифрованного файла +теперь является ассоциированными данными при шифровании. + @item MultiCast Discovery использует @verb{|ff02::4e4e:4350|} адрес вместо @verb{|ff02::1|}. diff --git a/doc/news.texi b/doc/news.texi index 7314d4e..e06cae8 100644 --- a/doc/news.texi +++ b/doc/news.texi @@ -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|}. diff --git a/doc/pkt.texi b/doc/pkt.texi index 567d3f7..b479113 100644 --- a/doc/pkt.texi +++ b/doc/pkt.texi @@ -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 diff --git a/src/pkt.go b/src/pkt.go index 6bc9c91..3aed5a6 100644 --- a/src/pkt.go +++ b/src/pkt.go @@ -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 } diff --git a/src/tx.go b/src/tx.go index 20482ef..f142d2f 100644 --- 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 } }() -- 2.44.0