From: Sergey Matveev Date: Thu, 13 Feb 2025 10:31:58 +0000 (+0300) Subject: Currently simplified binding X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=b393cc8dfd743cb3f22fcaa9152d24d5ecd280a4529303bce009ee43cd18b64f;p=keks.git Currently simplified binding --- diff --git a/go/cm/cmd/enctool/main.go b/go/cm/cmd/enctool/main.go index 6378a10..e5213a4 100644 --- a/go/cm/cmd/enctool/main.go +++ b/go/cm/cmd/enctool/main.go @@ -45,8 +45,6 @@ import ( "go.cypherpunks.su/keks/types" ) -const BindFdNum = 3 + 1 - func blake2bHash() hash.Hash { h, err := blake2b.New512(nil) if err != nil { @@ -82,7 +80,7 @@ func readPasswd(prompt string) (passwd []byte) { func main() { log.SetFlags(log.Lshortfile) flag.Usage = usage - setBind := flag.String("bind", "", "Set that /bind instead of autogeneration") + setSalt := flag.String("salt", "", "Set that /salt instead of autogeneration") includeTo := flag.Bool("include-to", false, `Include "to" field in KEMs`) passphrase := flag.Bool("p", false, "Use passphrase") balloonS := flag.Int("balloon-s", 1<<16, "Balloon's space cost") @@ -150,13 +148,6 @@ func main() { log.Fatal(err) } } - if encrypted.Bind == uuid.Nil { - log.Fatalln("unll bind") - } - if bindFd := os.NewFile(BindFdNum, "bind"); bindFd != nil { - bindFd.WriteString(encrypted.Bind.String() + "\n") - bindFd.Close() - } if encrypted.DEM.A != cm.ChaCha20Poly1305 { log.Fatalln("unsupported DEM:", encrypted.DEM.A) } @@ -184,7 +175,7 @@ func main() { balloon.H( blake2bHash, passwd, - append(encrypted.Bind[:], *kem.Salt...), + append(encrypted.Salt[:], *kem.Salt...), int(kem.Cost.S), int(kem.Cost.T), int(kem.Cost.P), ), cmenc.BalloonHKDFInfo, @@ -261,7 +252,7 @@ func main() { keySNTRUP[:], keyX25519, }, []byte{}) var prk []byte - prk, err = hkdf.Extract(blake2bHash, ikm, encrypted.Bind[:]) + prk, err = hkdf.Extract(blake2bHash, ikm, encrypted.Salt[:]) if err != nil { log.Fatal(err) } @@ -285,6 +276,7 @@ func main() { continue } cek = cekp + break } } case cm.ClassicMcEliece6960119X25519HKDFSHAKE256: @@ -313,7 +305,6 @@ func main() { if err != nil { log.Fatal(err) } - x25519 := ecdh.X25519() var ourX25519 *ecdh.PrivateKey ourX25519, err = x25519.NewPrivateKey(prv.V[len(prv.V)-32:]) @@ -355,7 +346,7 @@ func main() { }, []byte{}) var prk []byte prk, err = hkdf.Extract( - cmhash.NewSHAKE256, ikm, encrypted.Bind[:]) + cmhash.NewSHAKE256, ikm, encrypted.Salt[:]) if err != nil { log.Fatal(err) } @@ -380,6 +371,7 @@ func main() { continue } cek = cekp + break } } default: @@ -398,19 +390,15 @@ func main() { log.Fatal(err) } } else { - var binding uuid.UUID - if *setBind == "" { - binding, err = uuid.NewV7() + var salt uuid.UUID + if *setSalt == "" { + salt, err = uuid.NewRandom() } else { - binding, err = uuid.Parse(*setBind) + salt, err = uuid.Parse(*setSalt) } if err != nil { log.Fatal(err) } - if bindFd := os.NewFile(BindFdNum, "bind"); bindFd != nil { - bindFd.WriteString(binding.String() + "\n") - bindFd.Close() - } var kems []cmenc.KEM cek = make([]byte, chacha20poly1305.KeySize+chacha20poly1305.NonceSize) _, err = io.ReadFull(rand.Reader, cek) @@ -425,13 +413,13 @@ func main() { log.Fatal("passphrases do not match") } } - salt := make([]byte, cmenc.BalloonSaltLen) - if _, err = io.ReadFull(rand.Reader, salt); err != nil { + bSalt := make([]byte, cmenc.BalloonSaltLen) + if _, err = io.ReadFull(rand.Reader, bSalt); err != nil { log.Fatal(err) } kem := cmenc.KEM{ A: cm.BalloonBLAKE2bHKDF, - Salt: &salt, + Salt: &bSalt, Cost: &cmenc.BalloonCost{ S: uint64(*balloonS), T: uint64(*balloonT), @@ -444,7 +432,7 @@ func main() { blake2bHash, balloon.H(blake2bHash, passwd, - append(binding[:], salt...), + append(salt[:], bSalt...), *balloonS, *balloonT, *balloonP, ), cmenc.BalloonHKDFInfo, @@ -504,7 +492,7 @@ func main() { keySNTRUP[:], keyX25519, }, []byte{}) var prk []byte - prk, err = hkdf.Extract(blake2bHash, ikm, binding[:]) + prk, err = hkdf.Extract(blake2bHash, ikm, salt[:]) if err != nil { log.Fatal(err) } @@ -571,7 +559,7 @@ func main() { keyMcEliece[:], keyX25519, }, []byte{}) var prk []byte - prk, err = hkdf.Extract(cmhash.NewSHAKE256, ikm, binding[:]) + prk, err = hkdf.Extract(cmhash.NewSHAKE256, ikm, salt[:]) if err != nil { log.Fatal(err) } @@ -607,7 +595,7 @@ func main() { log.Fatal(err) } if _, err = keks.Encode(&hdr, &cmenc.Encrypted{ - Bind: binding, + Salt: salt, KEM: kems, DEM: cmenc.DEM{A: cm.ChaCha20Poly1305}, }, nil); err != nil { diff --git a/go/cm/cmd/enctool/pub.t b/go/cm/cmd/enctool/pub.t index 19d98cb..ec464d6 100755 --- a/go/cm/cmd/enctool/pub.t +++ b/go/cm/cmd/enctool/pub.t @@ -6,7 +6,6 @@ test_description="Check that basic public-key encryption functionality works" TMPDIR=${TMPDIR:-/tmp} dd if=/dev/urandom of=$TMPDIR/enc.data bs=300K count=1 2>/dev/null -bind=$(uuidgen) algo=mceliece6960119-x25519 algo0=$algo @@ -19,18 +18,15 @@ test_expect_success "$algo: pub generation" "keytool \ -algo $algo -ku kem -subj A=$algo \ -prv $TMPDIR/enc.$algo.prv -pub $TMPDIR/enc.$algo.pub" -test_expect_success "encrypting" "enctool -bind $bind \ +test_expect_success "encrypting" "enctool \ -pub $TMPDIR/enc.$algo0.pub -pub $TMPDIR/enc.$algo1.pub \ <$TMPDIR/enc.data >$TMPDIR/enc.enc" test_expect_success "any: decrypting" "enctool -d \ -prv $TMPDIR/enc.$algo0.prv -prv $TMPDIR/enc.$algo1.prv \ - <$TMPDIR/enc.enc >$TMPDIR/enc.data.got 4>$TMPDIR/bind.got" + <$TMPDIR/enc.enc >$TMPDIR/enc.data.got" test_expect_success "comparing" \ "test_cmp $TMPDIR/enc.data $TMPDIR/enc.data.got" -echo $bind >$TMPDIR/bind -test_expect_success "comparing bind" \ - "test_cmp $TMPDIR/bind $TMPDIR/bind.got" test_expect_success "$algo0: decrypting" "enctool -d \ -prv $TMPDIR/enc.$algo0.prv \ diff --git a/go/cm/cmd/enctool/usage.go b/go/cm/cmd/enctool/usage.go index 58fccfc..655cf38 100644 --- a/go/cm/cmd/enctool/usage.go +++ b/go/cm/cmd/enctool/usage.go @@ -24,13 +24,11 @@ import ( func usage() { fmt.Fprintf(os.Stderr, `Usage: Encrypt to recipient(s): - enctool -pub PUB [-pub ...] [-include-to] [-bind UUID] - DATA.encrypted [4>bind.value] + enctool -pub PUB [-pub ...] [-include-to] DATA.encrypted Encrypt on passphrase: - enctool -p [-bind UUID] [-balloon-s X] [-balloon-t X] [-balloon-p X] - DATA.encrypted [4>bind.value] + enctool -p [-balloon-s X] [-balloon-t X] [-balloon-p X] DATA.encrypted Decrypt by providing possible KEMs: - enctool -d [-p] [-prv PRV ...] DATA [4>bind.value] + enctool -d [-p] [-prv PRV ...] DATA `) flag.PrintDefaults() diff --git a/go/cm/cmd/sigtool/basic.t b/go/cm/cmd/sigtool/basic.t index 7c0b001..3a1e12d 100755 --- a/go/cm/cmd/sigtool/basic.t +++ b/go/cm/cmd/sigtool/basic.t @@ -15,15 +15,15 @@ test_expect_success "$keyalgo: pub generation" "keytool \ -algo $keyalgo -ku sig $subj \ -prv $TMPDIR/sign.$keyalgo.prv -pub $TMPDIR/sign.$keyalgo.pub" dd if=/dev/urandom of=$TMPDIR/sign.$keyalgo.data bs=300K count=1 2>/dev/null -bind="-encrypted-binding $(uuidgen)" -badBind="-encrypted-binding $(uuidgen)" +encTo="-encrypted-to $(uuidgen)" +badEncTo="-encrypted-to $(uuidgen)" for merkle in "" "-merkle" ; do algo=${keyalgo}${merkle} test_expect_success "$algo: signing" "sigtool $merkle \ -prv $TMPDIR/sign.$keyalgo.prv -pub $TMPDIR/sign.$keyalgo.pub -type $typ \ - $bind <$TMPDIR/sign.$keyalgo.data >$TMPDIR/sign.$algo.sig" + $encTo <$TMPDIR/sign.$keyalgo.data >$TMPDIR/sign.$algo.sig" test_expect_success "$algo: verifying" "sigtool \ -verify -pub $TMPDIR/sign.$keyalgo.pub -type $typ \ <$TMPDIR/sign.$algo.sig >$TMPDIR/sign.data.got" @@ -31,10 +31,10 @@ test_expect_success "$algo: comparing" \ "test_cmp $TMPDIR/sign.$keyalgo.data $TMPDIR/sign.data.got" test_expect_success "$algo: differing type" "! sigtool \ -verify -pub $TMPDIR/sign.$keyalgo.pub <$TMPDIR/sign.$algo.sig >/dev/null" -test_expect_success "$algo: good bind" "! sigtool \ - -verify -pub $TMPDIR/sign.$keyalgo.pub $bind <$TMPDIR/sign.$algo.sig >/dev/null" -test_expect_success "$algo: bad bind" "! sigtool \ - -verify -pub $TMPDIR/sign.$keyalgo.pub $badBind <$TMPDIR/sign.$algo.sig >/dev/null" +test_expect_success "$algo: good encTo" "! sigtool \ + -verify -pub $TMPDIR/sign.$keyalgo.pub $encTo <$TMPDIR/sign.$algo.sig >/dev/null" +test_expect_success "$algo: bad encTo" "! sigtool \ + -verify -pub $TMPDIR/sign.$keyalgo.pub $badEncTo <$TMPDIR/sign.$algo.sig >/dev/null" test_expect_success "$algo: detached signing" "sigtool -detached $merkle \ -prv $TMPDIR/sign.$keyalgo.prv -pub $TMPDIR/sign.$keyalgo.pub -type $typ \ @@ -44,10 +44,10 @@ test_expect_success "$algo: detached verifying" \ sigtool -detached -verify -pub $TMPDIR/sign.$keyalgo.pub -type $typ" test_expect_success "$algo: differing type" "! sigtool -detached \ -verify -pub $TMPDIR/sign.$keyalgo.pub <$TMPDIR/sign.$algo.detached.sig >/dev/null" -test_expect_success "$algo: good bind" "! sigtool -detached \ - -verify -pub $TMPDIR/sign.$keyalgo.pub $bind <$TMPDIR/sign.$algo.detached.sig >/dev/null" -test_expect_success "$algo: bad bind" "! sigtool -detached \ - -verify -pub $TMPDIR/sign.$keyalgo.pub $badBind <$TMPDIR/sign.$algo.detached.sig >/dev/null" +test_expect_success "$algo: good encTo" "! sigtool -detached \ + -verify -pub $TMPDIR/sign.$keyalgo.pub $encTo <$TMPDIR/sign.$algo.detached.sig >/dev/null" +test_expect_success "$algo: bad encTo" "! sigtool -detached \ + -verify -pub $TMPDIR/sign.$keyalgo.pub $badEncTo <$TMPDIR/sign.$algo.detached.sig >/dev/null" done diff --git a/go/cm/cmd/sigtool/main.go b/go/cm/cmd/sigtool/main.go index 1ed47f8..afc9192 100644 --- a/go/cm/cmd/sigtool/main.go +++ b/go/cm/cmd/sigtool/main.go @@ -23,6 +23,7 @@ import ( "io" "log" "os" + "slices" "time" "github.com/google/uuid" @@ -38,28 +39,25 @@ import ( const BlobChunkLen = 128 * 1024 func main() { + log.SetFlags(log.Lshortfile) flag.Usage = usage prvPath := flag.String("prv", "", "Path to private key file") pubPath := flag.String("pub", "", "Path to public key file") typ := flag.String("type", "data", "Set/check the load type") verify := flag.Bool("verify", false, "Do verification") - encryptedBindingHex := flag.String("encrypted-binding", "", - "Set/check encrypted-binding, UUID") + var encryptedTo []uuid.UUID + flag.Func("encrypted-to", "Set/check encrypted-to, UUID", func(v string) error { + to, err := uuid.Parse(v) + if err != nil { + return err + } + encryptedTo = append(encryptedTo, to) + return nil + }) detached := flag.Bool("detached", false, "Detached data mode") noWhen := flag.Bool("no-when", false, `Do not include "when"`) doMerkle := flag.Bool("merkle", false, "Use Merkle-tree based hasher") - flag.Parse() - log.SetFlags(log.Lshortfile) - - encryptedBinding := uuid.Nil - var err error - if *encryptedBindingHex != "" { - encryptedBinding, err = uuid.Parse(*encryptedBindingHex) - if err != nil { - log.Fatal(err) - } - } if *pubPath == "" { log.Fatal("no -pub is set") @@ -142,12 +140,19 @@ func main() { if signed.Load.T != *typ { log.Fatalln("differing load type:", signed.Load.T) } - if encryptedBinding != uuid.Nil { - if sig.TBS.EncryptedBinding == nil { - log.Fatal("missing encrypted-binding") + if len(encryptedTo) > 0 { + if sig.TBS.EncryptedTo == nil { + log.Fatal("missing encrypted-to") + } + found := false + for _, to := range *sig.TBS.EncryptedTo { + if slices.Contains(encryptedTo, to) { + found = true + break + } } - if *sig.TBS.EncryptedBinding != encryptedBinding { - log.Fatalln("differing encrypted-binding:", *sig.TBS.EncryptedBinding) + if !found { + log.Fatalln("corresponding encrypted-to not found") } } if prehash.T == "" { @@ -214,8 +219,8 @@ func main() { when := time.Now().UTC().Truncate(time.Millisecond) sigTbs.When = &when } - if encryptedBinding != uuid.Nil { - sigTbs.EncryptedBinding = &encryptedBinding + if len(encryptedTo) > 0 { + sigTbs.EncryptedTo = &encryptedTo } if err = signed.SignWith(pub.PubLoad(), signer, sigTbs); err != nil { log.Fatal(err) diff --git a/go/cm/cmd/sigtool/usage.go b/go/cm/cmd/sigtool/usage.go index c38a039..cad6ab1 100644 --- a/go/cm/cmd/sigtool/usage.go +++ b/go/cm/cmd/sigtool/usage.go @@ -23,12 +23,11 @@ import ( func usage() { fmt.Fprintf(os.Stderr, `Usage: - sigtool -prv PRV -pub PUB [-type TYPE] [bind] DATA.signed - sigtool -verify -pub PUB [-type TYPE] [bind] DATA - sigtool -detached -prv PRV -pub PUB [-type TYPE] [bind] DATA.signature - sigtool -detached -verify -pub PUB [-type TYPE] [bind] <(cat DATA.signature DATA) + sigtool -prv PRV -pub PUB [-type TYPE] DATA.signed + sigtool -verify -pub PUB [-type TYPE] DATA + sigtool -detached -prv PRV -pub PUB [-type TYPE] DATA.signature + sigtool -detached -verify -pub PUB [-type TYPE] <(cat DATA.signature DATA) -"bind" is optional -encrypted-binding flag. DATA.signed holds completely encapsulated DATA. -detached mode keeps DATA completely separate. diff --git a/go/cm/encrypted/kem.go b/go/cm/encrypted/kem.go index ff70920..497f4f6 100644 --- a/go/cm/encrypted/kem.go +++ b/go/cm/encrypted/kem.go @@ -29,7 +29,7 @@ type DEM struct { type Encrypted struct { DEM DEM `keks:"dem"` KEM []KEM `keks:"kem"` - Bind uuid.UUID `keks:"bind"` + Salt uuid.UUID `keks:"salt"` Ciphertext *keks.BlobChunked `keks:"ciphertext,omitempty"` } diff --git a/go/cm/signed.go b/go/cm/signed.go index ed821a0..7a67fb6 100644 --- a/go/cm/signed.go +++ b/go/cm/signed.go @@ -41,15 +41,15 @@ type SignedLoad struct { } type SigTBS struct { - CID *uuid.UUID `keks:"cid,omitempty"` - Exp *[]time.Time `keks:"exp,omitempty"` - When *time.Time `keks:"when,omitempty"` - EncryptedBinding *uuid.UUID `keks:"encrypted-binding,omitempty"` - SID uuid.UUID `keks:"sid"` + CID *uuid.UUID `keks:"cid,omitempty"` + Exp *[]time.Time `keks:"exp,omitempty"` + When *time.Time `keks:"when,omitempty"` + EncryptedTo *[]uuid.UUID `keks:"encrypted-to,omitempty"` + SID uuid.UUID `keks:"sid"` } type Sig struct { - TBS SigTBS `keks:"tbs,omitempty"` + TBS SigTBS `keks:"tbs"` PubLoc *[]string `keks:"pub-loc,omitempty"` Sign AV `keks:"sign"` } diff --git a/spec/format/encrypted.cddl b/spec/format/encrypted.cddl index 61ae8a3..45300ce 100644 --- a/spec/format/encrypted.cddl +++ b/spec/format/encrypted.cddl @@ -3,7 +3,7 @@ ai = text ; algorithm identifier cm-encrypted = { dem: dem, kem: [+ kem], - bind: uuid, + salt: uuid, ? ciphertext: blob, } @@ -40,19 +40,19 @@ kem-gost3410-hkdf-kexp15 = { cek: bytes, ukm: bytes, pub: bytes, - ? to: uuid, + ? to: uuid, ; recipient's public key id } kem-sntrup4591761-x25519-hkdf-blake2b = { a: "sntrup4591761-x25519-hkdf-blake2b", cek: bytes, encap: bytes, - ? to: uuid, + ? to: uuid, ; recipient's public key id } kem-mceliece6960119-x25519-hkdf-shake256 = { a: "mceliece6960119-x25519-hkdf-shake256 ", cek: bytes, encap: bytes, - ? to: uuid, + ? to: uuid, ; recipient's public key id } diff --git a/spec/format/encrypted.texi b/spec/format/encrypted.texi index ef227ca..c328a12 100644 --- a/spec/format/encrypted.texi +++ b/spec/format/encrypted.texi @@ -26,9 +26,7 @@ signatures at all. Optional @code{/kem/*/to}, public key's identifier, may provide a hint for quickly searching for the key on the recipient's side. -@code{/bind} value can be used to bind the encrypted -@code{@ref{cm-signed, cm-signed}} to the envelope. -Either UUIDv4 or UUIDv7 are recommended. +@code{/salt} is used in KEMs. Either UUIDv4 or UUIDv7 are recommended. @node cm-encrypted-chacha20poly1305 @cindex cm-encrypted-chacha20poly1305 @@ -82,13 +80,13 @@ Kenc || IV || Kauth = CEK Recipient map must also contain additional fields: @table @code - @item /to/*/cost/s: uint64 + @item /kem/*/cost/s: uint64 Balloon's space cost (buffer size, number of hash-output sized blocks). - @item /to/*/cost/t: uint64 + @item /kem/*/cost/t: uint64 Balloon's time cost (number of rounds). - @item /to/*/cost/p: uint64 + @item /kem/*/cost/p: uint64 Balloon's parallel cost (number of threads). - @item /to/*/salt: bin + @item /kem/*/salt: bin Salt. @end table @@ -99,7 +97,7 @@ Kenc || IV || Kauth = CEK @verbatim KEK = HKDF-Expand(BLAKE2b, - prk=balloon(BLAKE2b, passphrase, bind || salt, s, t, p), + 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 @@ -150,7 +148,7 @@ KExp15(KEKenc, KEKauth, IV, CEK) = CTR(Kenc, CEK || CMAC(Kauth, IV || CEK), IV=I @code{/kem/*/cek} is encrypted the following way: @verbatim -PRK = HKDF-Extract(BLAKE2b, salt=bind, +PRK = HKDF-Extract(BLAKE2b, salt=/salt, secret= sntrup4591761-sender-ciphertext || x25519-sender-public-key || @@ -183,7 +181,7 @@ ChaCha20-Poly1305(data=CEK, key=KEK, nonce=12*0x00, ad="") @code{/kem/*/cek} is encrypted the following way: @verbatim -PRK = HKDF-Extract(SHAKE256, salt=bind, +PRK = HKDF-Extract(SHAKE256, salt=/salt, secret= mceliece6960119-sender-ciphertext || x25519-sender-public-key || diff --git a/spec/format/signed.cddl b/spec/format/signed.cddl index 062f051..9a9eba3 100644 --- a/spec/format/signed.cddl +++ b/spec/format/signed.cddl @@ -20,7 +20,7 @@ url = text sig-tbs = { sid: uuid, ; signer's public key id - ? encrypted-binding: uuid, + ? encrypted-to: [+ uuid], ; recipient's public key ids ? when: tai64 / tai64n, * text => any } diff --git a/spec/format/signed.texi b/spec/format/signed.texi index 88fa8df..8600af2 100644 --- a/spec/format/signed.texi +++ b/spec/format/signed.texi @@ -43,8 +43,8 @@ help creating the whole verification chain. They are placed outside @code{/sigs}, because some of them may be shared among signers. If signed data is also intended to be @ref{cm-encrypted, encrypted}, -then @code{/sigs/*/tbs/encrypted-binding} should be set to -@ref{cm-encrypted, encrypted}'s @code{/bind} value. +then @code{/sigs/*/tbs/encrypted-to} should be set to corresponding +recipient's public key id(s). @node cm-signed-gost3410 @cindex cm-signed-gost3410