]> Cypherpunks repositories - keks.git/commitdiff
Revised key commitment
authorSergey Matveev <stargrave@stargrave.org>
Fri, 28 Feb 2025 13:53:47 +0000 (16:53 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Fri, 28 Feb 2025 13:54:47 +0000 (16:54 +0300)
go/cm/enc/chapoly/dem.go
spec/cm/dem-chapoly-krkc.texi

index 854a84d61acd725f1b64d105cd8d0d812194b0d90e276fed302180f124ea22ff..220693d7493b986a437af86fb230379ea0363731a1948c62770d06045891c226 100644 (file)
@@ -19,6 +19,7 @@ import (
        "bytes"
        "crypto/cipher"
        "crypto/hkdf"
+       "crypto/subtle"
        "errors"
        "hash"
        "io"
@@ -35,12 +36,16 @@ const (
        DEMAlgo       = "chapoly-krkc"
 )
 
+type keymat struct {
+       key []byte
+       iv  []byte
+}
+
 type job struct {
-       bufReady         chan struct{}
-       processed        chan struct{}
-       keyAndCommitment []byte
-       buf              []byte
-       tail             bool
+       bufReady  chan struct{}
+       processed chan error
+       keymat    keymat
+       buf       []byte
 }
 
 func blake2bHash() hash.Hash {
@@ -63,19 +68,26 @@ func do(
        }
        ready := make(chan *job, procs)
        dones := make(chan *job, procs)
-       keyAndCommitments := make(chan []byte, procs)
+       keymats := make(chan keymat, procs)
        go func() {
                ck := cek
-               var keyAndCommitment []byte
+               var key []byte
+               var iv []byte
                var errHKDF error
                for {
-                       keyAndCommitment, errHKDF = hkdf.Expand(
-                               blake2bHash, ck, "cm/encrypted/chapoly-krkc",
-                               chacha20poly1305.KeySize+CommitmentLen)
+                       key, errHKDF = hkdf.Expand(
+                               blake2bHash, ck, "cm/encrypted/chapoly-krkc/key",
+                               chacha20poly1305.KeySize)
+                       if errHKDF != nil {
+                               panic(errHKDF)
+                       }
+                       iv, errHKDF = hkdf.Expand(
+                               blake2bHash, ck, "cm/encrypted/chapoly-krkc/iv",
+                               chacha20poly1305.NonceSizeX)
                        if errHKDF != nil {
                                panic(errHKDF)
                        }
-                       keyAndCommitments <- keyAndCommitment
+                       keymats <- keymat{key: key, iv: iv}
                        ck, errHKDF = hkdf.Extract(blake2bHash, nil, ck)
                        if errHKDF != nil {
                                panic(errHKDF)
@@ -96,56 +108,61 @@ func do(
        if err != nil {
                return
        }
-       var errUnauth error
        for range procs {
                go func() {
-                       var ciph cipher.AEAD
                        j := job{
                                buf:       make([]byte, blobChunkLen),
                                bufReady:  make(chan struct{}),
-                               processed: make(chan struct{}),
+                               processed: make(chan error),
                        }
-                       nonce := make([]byte, chacha20poly1305.NonceSize)
                        ready <- &j
-                       var errOpen error
+                       var errJob error
+                       var ciph cipher.AEAD
+                       var com hash.Hash
+                       var tag []byte
+                       ourCom := make([]byte, CommitmentLen)
                        for {
                                <-j.bufReady
-                               ciph, err = chacha20poly1305.New(
-                                       j.keyAndCommitment[:chacha20poly1305.KeySize],
-                               )
-                               if err != nil {
-                                       panic(err)
+                               ciph, errJob = chacha20poly1305.NewX(j.keymat.key)
+                               if errJob != nil {
+                                       panic(errJob)
                                }
-                               if j.tail {
-                                       nonce[len(nonce)-1] = 0x01
+                               com, errJob = blake2b.New(CommitmentLen, nil)
+                               if errJob != nil {
+                                       panic(errJob)
                                }
                                if seal {
                                        ciph.Seal(
                                                j.buf[:0],
-                                               nonce,
+                                               j.keymat.iv,
                                                j.buf[:len(j.buf)-chacha20poly1305.Overhead-CommitmentLen],
                                                nil,
                                        )
-                                       copy(
-                                               j.buf[len(j.buf)-CommitmentLen:],
-                                               j.keyAndCommitment[chacha20poly1305.KeySize:],
-                                       )
+                                       tag = j.buf[len(j.buf)-chacha20poly1305.Overhead-CommitmentLen:]
+                                       tag = tag[:chacha20poly1305.Overhead]
+                                       com.Write(j.keymat.key)
+                                       com.Write(j.keymat.iv)
+                                       com.Write(tag)
+                                       com.Sum(j.buf[:len(j.buf)-CommitmentLen])
                                } else {
-                                       if bytes.Equal(
+                                       tag = j.buf[len(j.buf)-chacha20poly1305.Overhead-CommitmentLen:]
+                                       tag = tag[:chacha20poly1305.Overhead]
+                                       com.Write(j.keymat.key)
+                                       com.Write(j.keymat.iv)
+                                       com.Write(tag)
+                                       ourCom = com.Sum(ourCom[:0])
+                                       if subtle.ConstantTimeCompare(
+                                               ourCom,
                                                j.buf[len(j.buf)-CommitmentLen:],
-                                               j.keyAndCommitment[chacha20poly1305.KeySize:],
-                                       ) {
-                                               j.buf, errOpen = ciph.Open(
-                                                       j.buf[:0], nonce, j.buf[:len(j.buf)-CommitmentLen], nil,
+                                       ) == 1 {
+                                               j.buf, errJob = ciph.Open(
+                                                       j.buf[:0], j.keymat.iv, j.buf[:len(j.buf)-CommitmentLen], nil,
                                                )
-                                               if errOpen == nil {
-                                                       errUnauth = errOpen
-                                               }
                                        } else {
-                                               errUnauth = errors.New("commitment differs")
+                                               errJob = errors.New("commitment differs")
                                        }
                                }
-                               j.processed <- struct{}{}
+                               j.processed <- errJob
                        }
                }()
        }
@@ -154,7 +171,12 @@ func do(
        go func() {
                var n int64
                for j := range dones {
-                       <-j.processed
+                       errW = <-j.processed
+                       if errW != nil {
+                               j.buf = j.buf[:blobChunkLen]
+                               ready <- j
+                               break
+                       }
                        if seal {
                                if blob {
                                        n, errW = keks.BinEncode(w, j.buf)
@@ -182,12 +204,13 @@ func do(
        var eof bool
        var n int
        var j *job
+       var tailMet bool
        for !eof {
-               if errUnauth != nil {
-                       err = errUnauth
-                       close(dones)
-                       <-finished
+               select {
+               case <-finished:
+                       err = errW
                        return
+               default:
                }
                j = <-ready
                if seal {
@@ -220,19 +243,24 @@ func do(
                if !seal && n == 0 {
                        break
                }
+               j.keymat = <-keymats
                if (seal && n < ChunkLen) || (!seal && n < blobChunkLen) {
-                       j.tail = true
+                       j.keymat.iv[chacha20poly1305.NonceSizeX-1] |= 0x01
+                       tailMet = true
+               } else {
+                       j.keymat.iv[chacha20poly1305.NonceSizeX-1] &= 0xFE
                }
-               j.keyAndCommitment = <-keyAndCommitments
                j.bufReady <- struct{}{}
                dones <- j
        }
        close(dones)
        <-finished
-       if errUnauth == nil {
-               err = errW
-       } else {
-               err = errUnauth
+       err = errW
+       if err != nil {
+               return
+       }
+       if !tailMet {
+               err = errors.New("no tail met")
        }
        return
 }
index 67ad95f1e2b4a8133c68448e4f65b6117a7e790b358209eb25d506817ce3062f..87783b2b64b66ce2b36c18d41cd0b3e69b77384c3d1508bc19c0f415afee00cb 100644 (file)
@@ -11,16 +11,17 @@ Data is split on 128 KiB chunks, each of which is encrypted the following way:
 @verbatim
 CK0 = CEK
 CKi = HKDF-Extract(BLAKE2b, salt="", ikm=CK{i-1})
-KEY || COMMITMENT = HKDF-Expand(BLAKE2b, prk=CKi, info="cm/encrypted/chapoly-krkc")
-ChaCha20-Poly1305(key=KEY, ad="", nonce=11*0x00 || tail-flag, data=chunk) || COMMITMENT
+KEY = HKDF-Expand(BLAKE2b, prk=CKi, info="cm/encrypted/chapoly-krkc/key")
+IV = HKDF-Expand(BLAKE2b, prk=CKi, info="cm/encrypted/chapoly-krkc/iv", len=24)
+if last chunk { IV[23] |= 0x01 } else { IV[23] &= 0xFE }
+CIPHERTEXT || TAG = XChaCha20-Poly1305(key=KEY, ad="", nonce=IV, data=chunk)
+COMMITMENT = BLAKE2b-256(KEY || IV || TAG)
+CIPHERTEXT || TAG || COMMITMENT
 @end verbatim
 
-Chaining key (CK) advances with every chunk. 256-bits encryption key and
-key commitment are derived from the chaining key.
+Chaining key (CK) advances with every chunk. 256-bit encryption key and
+randomised 192-bit nonce (initialisation vector) are derived from it.
 
-@code{tail-flag} is a byte indicating if that is the last chunk in the
-payload. It equals to 0x01 for the last chunk and to 0x00 for other
-ones. Last chunk should be smaller than previous ones, maybe (payload)
-even empty.
+Nonce's lowest bit is set only if this is the last chunk we encrypting.
 
 @code{/payload}'s chunk length equals to 128KiB+16+32 bytes.