]> Cypherpunks repositories - keks.git/commitdiff
Do not differentiate KEM and DEM ChaPoly usage
authorSergey Matveev <stargrave@stargrave.org>
Sat, 15 Feb 2025 08:13:00 +0000 (11:13 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sat, 15 Feb 2025 08:30:23 +0000 (11:30 +0300)
go/cm/cmd/enctool/main.go
go/cm/enc/chapoly/dem.go [moved from go/cm/enc/chapoly/dem/dem.go with 72% similarity]
go/cm/enc/chapoly/kem/kem.go [deleted file]
spec/cm/encrypted.texi

index c067f9c669492afdd5dbb15113b1a85933670178763c5c2e1e967b5557008cf9..3c1aaa29ae0533701047487546fbbbf463cd38b73707423f0cc6a3c48b90f278 100644 (file)
@@ -33,14 +33,12 @@ import (
        "github.com/google/uuid"
        "go.cypherpunks.su/balloon/v3"
        "golang.org/x/crypto/blake2b"
-       "golang.org/x/crypto/chacha20poly1305"
        "golang.org/x/term"
 
        "go.cypherpunks.su/keks"
        "go.cypherpunks.su/keks/cm"
        cmenc "go.cypherpunks.su/keks/cm/enc"
-       chaPolyDEM "go.cypherpunks.su/keks/cm/enc/chapoly/dem"
-       chaPolyKEM "go.cypherpunks.su/keks/cm/enc/chapoly/kem"
+       chaPoly "go.cypherpunks.su/keks/cm/enc/chapoly"
        mceliece6960119x25519 "go.cypherpunks.su/keks/cm/enc/mceliece6960119-x25519"
        mceliece6960119 "go.cypherpunks.su/keks/cm/enc/mceliece6960119-x25519/mceliece6960119"
        sntrup4591761x25519 "go.cypherpunks.su/keks/cm/enc/sntrup4591761-x25519"
@@ -192,21 +190,22 @@ func main() {
                                                        int(kem.Cost.S), int(kem.Cost.T), int(kem.Cost.P),
                                                ),
                                                cmenc.BalloonHKDFInfo,
-                                               chacha20poly1305.KeySize,
+                                               chaPoly.KeyLen,
                                        )
                                        if err != nil {
                                                log.Fatal(err)
                                        }
-                                       var cekp []byte
-                                       cekp, err = chaPolyKEM.Open(
-                                               kek, kem.CEK,
-                                               chacha20poly1305.KeySize+chacha20poly1305.NonceSize,
-                                       )
+                                       var cekp bytes.Buffer
+                                       _, err = chaPoly.Open(&cekp, bytes.NewReader(kem.CEK), kek, 1)
                                        if err != nil {
                                                log.Println(kemIdx, kem.A, err, ", skipping")
                                                continue
                                        }
-                                       cek = cekp
+                                       if cekp.Len() != chaPoly.KeyLen {
+                                               log.Println(kemIdx, kem.A, "wrong key len, skipping")
+                                               continue
+                                       }
+                                       cek = cekp.Bytes()
                                }
                        case sntrup4591761x25519.SNTRUP4591761X25519HKDFBLAKE2b:
                                if len(prvs) == 0 {
@@ -274,21 +273,22 @@ func main() {
                                                        blake2bHash,
                                                        prk,
                                                        cmenc.SNTRUP4591761X25519Info,
-                                                       chacha20poly1305.KeySize,
+                                                       chaPoly.KeyLen,
                                                )
                                                if err != nil {
                                                        log.Fatal(err)
                                                }
-                                               var cekp []byte
-                                               cekp, err = chaPolyKEM.Open(
-                                                       kek, kem.CEK,
-                                                       chacha20poly1305.KeySize+chacha20poly1305.NonceSize,
-                                               )
+                                               var cekp bytes.Buffer
+                                               _, err = chaPoly.Open(&cekp, bytes.NewReader(kem.CEK), kek, 1)
                                                if err != nil {
                                                        log.Println(kemIdx, kem.A, err, ", skipping")
                                                        continue
                                                }
-                                               cek = cekp
+                                               if cekp.Len() != chaPoly.KeyLen {
+                                                       log.Println(kemIdx, kem.A, "wrong key len, skipping")
+                                                       continue
+                                               }
+                                               cek = cekp.Bytes()
                                                break
                                        }
                                }
@@ -367,22 +367,22 @@ func main() {
                                                        cmhash.NewSHAKE256,
                                                        prk,
                                                        cmenc.ClassicMcEliece6960119X25519Info,
-                                                       chacha20poly1305.KeySize,
+                                                       chaPoly.KeyLen,
                                                )
                                                if err != nil {
                                                        log.Fatal(err)
                                                }
-                                               var cekp []byte
-                                               cekp, err = chaPolyKEM.Open(
-                                                       kek[:chacha20poly1305.KeySize],
-                                                       kem.CEK,
-                                                       chacha20poly1305.KeySize+chacha20poly1305.NonceSize,
-                                               )
+                                               var cekp bytes.Buffer
+                                               _, err = chaPoly.Open(&cekp, bytes.NewReader(kem.CEK), kek, 1)
                                                if err != nil {
                                                        log.Println(kemIdx, kem.A, err, ", skipping")
                                                        continue
                                                }
-                                               cek = cekp
+                                               if cekp.Len() != chaPoly.KeyLen {
+                                                       log.Println(kemIdx, kem.A, "wrong key len, skipping")
+                                                       continue
+                                               }
+                                               cek = cekp.Bytes()
                                                break
                                        }
                                }
@@ -397,7 +397,7 @@ func main() {
                if cek == nil {
                        log.Fatal("no KEMs processed")
                }
-               _, err = chaPolyDEM.Open(os.Stdout, os.Stdin, cek, *parallel)
+               _, err = chaPoly.Open(os.Stdout, os.Stdin, cek, *parallel)
                if err != nil {
                        log.Fatal(err)
                }
@@ -412,7 +412,7 @@ func main() {
                        log.Fatal(err)
                }
                var kems []cmenc.KEM
-               cek = make([]byte, chacha20poly1305.KeySize+chacha20poly1305.NonceSize)
+               cek = make([]byte, chaPoly.KeyLen)
                rand.Read(cek)
                if *passphrase {
                        passwd := readPasswd("Passphrase:")
@@ -443,15 +443,17 @@ func main() {
                                                *balloonS, *balloonT, *balloonP,
                                        ),
                                        cmenc.BalloonHKDFInfo,
-                                       chacha20poly1305.KeySize,
+                                       chaPoly.KeyLen,
                                )
                                if err != nil {
                                        log.Fatal(err)
                                }
-                               kem.CEK, err = chaPolyKEM.Seal(kek, cek)
+                               var cekp bytes.Buffer
+                               _, err = chaPoly.Seal(&cekp, bytes.NewReader(cek), kek, 1)
                                if err != nil {
                                        log.Fatal(err)
                                }
+                               kem.CEK = cekp.Bytes()
                        }
                        kems = append(kems, kem)
                }
@@ -508,15 +510,17 @@ func main() {
                                                blake2bHash,
                                                prk,
                                                cmenc.SNTRUP4591761X25519Info,
-                                               chacha20poly1305.KeySize,
+                                               chaPoly.KeyLen,
                                        )
                                        if err != nil {
                                                log.Fatal(err)
                                        }
-                                       kem.CEK, err = chaPolyKEM.Seal(kek, cek)
+                                       var cekp bytes.Buffer
+                                       _, err = chaPoly.Seal(&cekp, bytes.NewReader(cek), kek, 1)
                                        if err != nil {
                                                log.Fatal(err)
                                        }
+                                       kem.CEK = cekp.Bytes()
                                }
                                if *includeTo {
                                        kem.To = &pub.Id
@@ -574,17 +578,17 @@ func main() {
                                                cmhash.NewSHAKE256,
                                                prk,
                                                cmenc.ClassicMcEliece6960119X25519Info,
-                                               chacha20poly1305.KeySize,
+                                               chaPoly.KeyLen,
                                        )
                                        if err != nil {
                                                log.Fatal(err)
                                        }
-                                       kem.CEK, err = chaPolyKEM.Seal(
-                                               kek[:chacha20poly1305.KeySize], cek,
-                                       )
+                                       var cekp bytes.Buffer
+                                       _, err = chaPoly.Seal(&cekp, bytes.NewReader(cek), kek, 1)
                                        if err != nil {
                                                log.Fatal(err)
                                        }
+                                       kem.CEK = cekp.Bytes()
                                }
                                if *includeTo {
                                        kem.To = &pub.Id
@@ -613,7 +617,7 @@ func main() {
                                log.Fatal(err)
                        }
                }
-               if _, err = chaPolyDEM.Seal(os.Stdout, os.Stdin, cek, *parallel); err != nil {
+               if _, err = chaPoly.Seal(os.Stdout, os.Stdin, cek, *parallel); err != nil {
                        log.Fatal(err)
                }
        }
similarity index 72%
rename from go/cm/enc/chapoly/dem/dem.go
rename to go/cm/enc/chapoly/dem.go
index c8a903379406c495eec92e756a0baad0a655b4fb2a99de2b323ad9b520678d57..c2ec534c40fdca92ce8237e2fbbf2c2f6217e3a61de875bd6d259d30ca2d804f 100644 (file)
@@ -1,4 +1,4 @@
-// enctool -- dealing with KEKS-encoded cm-encrypted utility
+// GoKEKS/CM -- KEKS-encoded cryptographic messages
 // Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
@@ -27,8 +27,9 @@ import (
 )
 
 const (
-       ChaPolyChunkLen = 128 * 1024
-       ChaPolyPadLen   = 32
+       ChunkLen = 128 * 1024
+       PadLen   = 32
+       KeyLen   = chacha20poly1305.KeySize + chacha20poly1305.NonceSize
 )
 
 func incr(data []byte) {
@@ -44,22 +45,23 @@ type job struct {
        bufReady  chan struct{}
        processed chan struct{}
        buf       []byte
-       ctr       []byte
+       nonce     []byte
 }
 
 func do(
        w io.Writer,
        seal bool,
        r io.Reader,
-       cek []byte,
+       key []byte,
        procs int,
 ) (total int64, err error) {
-       if len(cek) != chacha20poly1305.KeySize+chacha20poly1305.NonceSize {
+       if len(key) != chacha20poly1305.KeySize+chacha20poly1305.NonceSize {
                return 0, errors.New("wrong CEK len")
        }
        ready := make(chan *job, procs)
        dones := make(chan *job, procs)
-       key, iv := cek[:chacha20poly1305.KeySize], cek[chacha20poly1305.KeySize:]
+       var iv []byte
+       key, iv = key[:chacha20poly1305.KeySize], key[chacha20poly1305.KeySize:]
        var ctr []byte
        var overhead int
        {
@@ -71,17 +73,17 @@ func do(
                ctr = make([]byte, ciph.NonceSize())
                overhead = ciph.Overhead()
        }
-       chunkLen := ChaPolyPadLen + ChaPolyChunkLen + overhead
+       blobChunkLen := PadLen + ChunkLen + overhead
        var blobDecoder *keks.BlobDecoder
        if seal {
-               total, err = keks.BlobAtomEncode(w, int64(chunkLen))
+               total, err = keks.BlobAtomEncode(w, int64(blobChunkLen))
        } else {
-               blobDecoder, err = keks.NewBlobDecoder(r, int64(chunkLen))
+               blobDecoder, err = keks.NewBlobDecoder(r, int64(blobChunkLen))
        }
        if err != nil {
                return
        }
-       chaPolyPad := make([]byte, ChaPolyPadLen)
+       chaPolyPad := make([]byte, PadLen)
        var errUnauth error
        for range procs {
                go func() {
@@ -91,8 +93,8 @@ func do(
                                panic(err)
                        }
                        j := job{
-                               buf:       make([]byte, chunkLen),
-                               ctr:       make([]byte, ciph.NonceSize()),
+                               buf:       make([]byte, blobChunkLen),
+                               nonce:     make([]byte, ciph.NonceSize()),
                                bufReady:  make(chan struct{}),
                                processed: make(chan struct{}),
                        }
@@ -101,16 +103,12 @@ func do(
                        var errOpen error
                        for {
                                <-j.bufReady
-                               subtle.XORBytes(nonce, j.ctr, iv)
                                if seal {
                                        ciph.Seal(j.buf[:0], nonce, j.buf[:len(j.buf)-overhead], nil)
                                } else {
                                        j.buf, errOpen = ciph.Open(j.buf[:0], nonce, j.buf, nil)
                                        if errOpen == nil {
-                                               if subtle.ConstantTimeCompare(
-                                                       j.buf[:ChaPolyPadLen],
-                                                       chaPolyPad,
-                                               ) != 1 {
+                                               if subtle.ConstantTimeCompare(j.buf[:PadLen], chaPolyPad) != 1 {
                                                        errUnauth = errors.New("bad pad")
                                                }
                                        } else {
@@ -133,14 +131,14 @@ func do(
                                if len(j.buf) == 0 {
                                        n = 0
                                } else {
-                                       n, errW = io.Copy(w, bytes.NewReader(j.buf[ChaPolyPadLen:]))
+                                       n, errW = io.Copy(w, bytes.NewReader(j.buf[PadLen:]))
                                }
                        }
                        total += n
                        if errW != nil {
                                break
                        }
-                       j.buf = j.buf[:chunkLen]
+                       j.buf = j.buf[:blobChunkLen]
                        ready <- j
                }
                close(finished)
@@ -159,7 +157,7 @@ func do(
                }
                j = <-ready
                if seal {
-                       n, errR = io.ReadFull(r, j.buf[ChaPolyPadLen:ChaPolyPadLen+ChaPolyChunkLen])
+                       n, errR = io.ReadFull(r, j.buf[PadLen:PadLen+ChunkLen])
                } else {
                        chunk, errR = blobDecoder.Next()
                        copy(j.buf, chunk)
@@ -177,19 +175,19 @@ func do(
                        errR = nil
                        eof = true
                        if seal {
-                               j.buf = j.buf[:ChaPolyPadLen+n+overhead]
+                               j.buf = j.buf[:PadLen+n+overhead]
                        }
                }
                if seal {
-                       clear(j.buf[:ChaPolyPadLen])
+                       clear(j.buf[:PadLen])
                } else if n == 0 {
                        break
                }
-               if (seal && n < ChaPolyChunkLen) || (!seal && n < chunkLen) {
+               if (seal && n < ChunkLen) || (!seal && n < blobChunkLen) {
                        ctr[len(ctr)-1] = 0x01
                }
+               subtle.XORBytes(j.nonce, ctr, iv)
                incr(ctr[:len(ctr)-1])
-               copy(j.ctr, ctr)
                j.bufReady <- struct{}{}
                dones <- j
        }
@@ -203,10 +201,10 @@ func do(
        return
 }
 
-func Seal(w io.Writer, r io.Reader, cek []byte, procs int) (total int64, err error) {
-       return do(w, true, r, cek, procs)
+func Seal(w io.Writer, r io.Reader, key []byte, procs int) (total int64, err error) {
+       return do(w, true, r, key, procs)
 }
 
-func Open(w io.Writer, r io.Reader, cek []byte, procs int) (total int64, err error) {
-       return do(w, false, r, cek, procs)
+func Open(w io.Writer, r io.Reader, key []byte, procs int) (total int64, err error) {
+       return do(w, false, r, key, procs)
 }
diff --git a/go/cm/enc/chapoly/kem/kem.go b/go/cm/enc/chapoly/kem/kem.go
deleted file mode 100644 (file)
index c400ba6..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-// GoKEKS/CM -- KEKS-encoded cryptographic messages
-// Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as
-// published by the Free Software Foundation, version 3 of the License.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-package kem
-
-import (
-       "errors"
-
-       "golang.org/x/crypto/chacha20poly1305"
-)
-
-func Seal(kek, cek []byte) ([]byte, error) {
-       ciph, err := chacha20poly1305.New(kek)
-       if err != nil {
-               return nil, err
-       }
-       nonce := make([]byte, ciph.NonceSize())
-       return ciph.Seal(nil, nonce, cek, nil), nil
-}
-
-func Open(kek, ciphertext []byte, cekLenExpected int) ([]byte, error) {
-       ciph, err := chacha20poly1305.New(kek)
-       if err != nil {
-               return nil, err
-       }
-       nonce := make([]byte, ciph.NonceSize())
-       var cek []byte
-       cek, err = ciph.Open(nil, nonce, ciphertext, nil)
-       if err != nil {
-               return nil, err
-       }
-       if len(cek) != cekLenExpected {
-               return nil, errors.New("invalid CEK len")
-       }
-       return cek, nil
-}
index cd2d0560c8532b0beda4541ff85504c1da28f74b249d6f7fc11812f41536b81f..c32f5c6327fb430956bc4a29eab9c5b0e76ff672ebd0fd8006ca18dc4bb81fbe 100644 (file)
@@ -52,7 +52,7 @@ ChaCha20-Poly1305(key=KEY, ad="",
 
     @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 even empty.
+    Last chunk should be smaller than previous ones, maybe (payload) even empty.
 
     @code{/ciphertext}'s chunk length equals to 32+128KiB+16 bytes.
 
@@ -97,13 +97,14 @@ Kenc || IV || Kauth = CEK
     @url{https://crypto.stanford.edu/balloon/, Balloon} memory-hardened
     password hasher must be used with BLAKE2b-256 hash.
 
-    @code{/kem/*/cek} is encrypted the following way:
+    @code{/kem/*/cek} is encrypted with
+    @ref{cm-encrypted-chacha20poly1305} algorithm, where counter is
+    zero, tail-flag is set and CEK is KEK:
 
 @verbatim
 KEK = HKDF-Expand(BLAKE2b,
     prk=balloon(BLAKE2b, passphrase, /salt || /kem/salt, s, t, p),
     info="keks/cm/encrypted/balloon-blake2b-hkdf")
-ChaCha20-Poly1305(data=CEK, key=KEK, nonce=12*0x00, ad="")
 @end verbatim
 
 @node cm-encrypted-gost3410-hkdf-kexp15
@@ -150,8 +151,7 @@ KExp15(KEKenc, KEKauth, IV, CEK) = CTR(Kenc, CEK || CMAC(Kauth, IV || CEK), IV=I
 
     Recipient performs Curve25519 and SNTRUP computation to
     derive/decapsulate two 32-byte shared keys. Then it combines
-    them to get the decryption key of the CEK.
-    @code{/kem/*/cek} is encrypted the following way:
+    them to get the KEK decryption key of the CEK.
 
 @verbatim
 PRK = HKDF-Extract(BLAKE2b, salt=/salt,
@@ -164,9 +164,12 @@ PRK = HKDF-Extract(BLAKE2b, salt=/salt,
         x25519-shared-key)
 KEK = HKDF-Expand(BLAKE2b, prk=PRK,
     info="keks/cm/encrypted/sntrup4591761-x25519-hkdf-blake2b")
-ChaCha20-Poly1305(data=CEK, key=KEK, nonce=12*0x00, ad="")
 @end verbatim
 
+    @code{/kem/*/cek} is encrypted with
+    @ref{cm-encrypted-chacha20poly1305} algorithm, where counter is
+    zero, tail-flag is set and CEK is KEK.
+
 @node cm-encrypted-mceliece6960119-x25519-hkdf-shake256
 @cindex cm-encrypted-mceliece6960119-x25519-hkdf-shake256
 @nodedescription cm/encrypted with Classic McEliece 6960-119+Curve25519+HKDF-SHAKE256 KEM
@@ -184,8 +187,7 @@ ChaCha20-Poly1305(data=CEK, key=KEK, nonce=12*0x00, ad="")
 
     Recipient performs Curve25519 and Classic McEliece computation to
     derive/decapsulate two 32-byte shared keys. Then it combines
-    them to get the decryption key of the CEK.
-    @code{/kem/*/cek} is encrypted the following way:
+    them to get the KEK decryption key of the CEK.
 
 @verbatim
 PRK = HKDF-Extract(SHAKE256, salt=/salt,
@@ -198,5 +200,8 @@ PRK = HKDF-Extract(SHAKE256, salt=/salt,
         x25519-shared-key)[:32]
 KEK = HKDF-Expand(SHAKE256, prk=PRK,
     info="keks/cm/encrypted/mceliece6960119-x25519-hkdf-shake256")
-ChaCha20-Poly1305(data=CEK, key=KEK, nonce=12*0x00, ad="")
 @end verbatim
+
+    @code{/kem/*/cek} is encrypted with
+    @ref{cm-encrypted-chacha20poly1305} algorithm, where counter is
+    zero, tail-flag is set and CEK is KEK.