]> Cypherpunks repositories - keks.git/commitdiff
sphincs+-shake-256s
authorSergey Matveev <stargrave@stargrave.org>
Wed, 16 Apr 2025 18:49:54 +0000 (21:49 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Thu, 17 Apr 2025 06:16:48 +0000 (09:16 +0300)
15 files changed:
go/cm/cmd/cmkeytool/certification.t
go/cm/cmd/cmkeytool/main.go
go/cm/cmd/cmsigtool/basic.t
go/cm/go.mod
go/cm/go.sum
go/cm/hash/algo.go
go/cm/hash/shake.go
go/cm/sign/prv.go
go/cm/sign/pub.go
go/cm/sign/spx/kp.go [new file with mode: 0644]
go/cm/sign/spx/signer.go [new file with mode: 0644]
go/cm/sign/spx/verify.go [new file with mode: 0644]
spec/cm/prv.texi
spec/cm/pub.texi
spec/cm/signed.texi

index e71bed137e684832302b271a2704d0539a4e1cb30f146bcce67d50b77ee3669e..7c03dc66cea3941973a9fa8aee04979730d5d09b0b74a3cb577a1705fbe75cbd 100755 (executable)
@@ -6,7 +6,8 @@ test_description="Check certification"
 TMPDIR=${TMPDIR:-/tmp}
 
 echo "gost3410-512C gost3410-256A
-ed25519-blake2b ed25519-blake2b" | while read caAlgo eeAlgo ; do
+ed25519-blake2b ed25519-blake2b
+sphincs+-shake-256s sphincs+-shake-256s" | while read caAlgo eeAlgo ; do
 
 sub="-sub CN=CA -sub C=RU"
 test_expect_success "$caAlgo: CA load generation" "cmkeytool \
index 785dee09f89b2c766bfe26864c7a322f6c652ab49382368bda7f71f7329c80ad..077b7cf476a1d0ac4cbcc209e29129d3095afe4d16f5486ec66c5d4c23e20f99 100644 (file)
@@ -36,6 +36,7 @@ import (
        "go.cypherpunks.su/keks/cm/sign"
        ed25519blake2b "go.cypherpunks.su/keks/cm/sign/ed25519-blake2b"
        "go.cypherpunks.su/keks/cm/sign/gost"
+       "go.cypherpunks.su/keks/cm/sign/spx"
 )
 
 const (
@@ -97,6 +98,7 @@ func main() {
                        gost.GOST3410512C,
                        sntrup4591761x25519.SNTRUP4591761X25519,
                        mceliece6960119x25519.ClassicMcEliece6960119X25519,
+                       spx.SPHINCSPlusSHAKE256s,
                }
                sort.Strings(algos)
                for _, s := range algos {
@@ -178,6 +180,8 @@ func main() {
                        prvRaw, pub, err = sntrup4591761x25519.NewKeypair()
                case mceliece6960119x25519.ClassicMcEliece6960119X25519:
                        prvRaw, pub, err = mceliece6960119x25519.NewKeypair()
+               case spx.SPHINCSPlusSHAKE256s:
+                       _, prvRaw, pub, err = spx.NewKeypair(*algo)
                default:
                        err = errors.New("unknown -algo specified")
                }
@@ -207,7 +211,7 @@ func main() {
                                hasher = cmhash.ByName(cmhash.BLAKE2b256)
                        case gost.GOST3410256A, gost.GOST3410512C:
                                hasher = cmhash.ByName(cmhash.Streebog256)
-                       case mceliece6960119x25519.ClassicMcEliece6960119X25519:
+                       case mceliece6960119x25519.ClassicMcEliece6960119X25519, spx.SPHINCSPlusSHAKE256s:
                                hasher = cmhash.ByName(cmhash.SHAKE128)
                        default:
                                log.Fatal("unsupported algorithm")
index 8e3cb3be9461ce1ef738326c0d15b3d65bfce497957d66c44362ffd4fd8ee452..87bd2841e5704807233f3fc3086bf6b67691b6633349e9ef87725f4dee832456 100755 (executable)
@@ -7,7 +7,8 @@ TMPDIR=${TMPDIR:-/tmp}
 
 echo "gost3410-512C
 gost3410-256A
-ed25519-blake2b" | while read keyalgo ; do
+ed25519-blake2b
+sphincs+-shake-256s" | while read keyalgo ; do
 
 sub="-sub what=ever"
 typ="some-different-type"
index 6dce99a86f03c28dbc8c9411efd9b401b952649933e7ea70b432c91f96ec5e01..c14f3ae8f968e3540eaf33f5fcab0bf41461a9e5eba1448990a667cf18ae8c2c 100644 (file)
@@ -5,6 +5,7 @@ go 1.24.0
 require (
        github.com/companyzero/sntrup4591761 v0.0.0-20220309191932-9e0f3af2f07a
        github.com/google/uuid v1.6.0
+       github.com/kasperdi/SPHINCSPLUS-golang v0.0.0-20231223193046-84468b93f7e9
        go.cypherpunks.su/balloon/v3 v3.0.0
        go.cypherpunks.su/gogost/v6 v6.1.0
        go.cypherpunks.su/keks v0.0.0-00010101000000-000000000000
index 4f4adc1a64eb9a292e691c9be9db42c397a26c485fc305d0929b46b7a3434ea8..85ba6bb688ced023f467cf21111b0ed26315e75739764957a85ffed965b0dd55 100644 (file)
@@ -2,6 +2,8 @@ github.com/companyzero/sntrup4591761 v0.0.0-20220309191932-9e0f3af2f07a h1:clYxJ
 github.com/companyzero/sntrup4591761 v0.0.0-20220309191932-9e0f3af2f07a/go.mod h1:z/9Ck1EDixEbBbZ2KH2qNHekEmDLTOZ+FyoIPWWSVOI=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/kasperdi/SPHINCSPLUS-golang v0.0.0-20231223193046-84468b93f7e9 h1:G8fshCtNb60L5IM2tuYD81uh6YQFqJ78MAGUCMks7Bg=
+github.com/kasperdi/SPHINCSPLUS-golang v0.0.0-20231223193046-84468b93f7e9/go.mod h1:XWeSWo+UqzMi1uh/Td/gKlVHaPQjUj92s3omn7eccUM=
 github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 go.cypherpunks.su/balloon/v3 v3.0.0 h1:80JUfOvjEgeuQlZ8biZarbuld0T9L/6gbC2DAZAZncI=
index 073f1dcc6f5a9cb696f9887952b28ae215002e5e4fce2613ef1fdfdab13fe39a..58194000ce82a3a3ab504a8c3328ff453b837f4f3718eaea7db151f41239a9f2 100644 (file)
@@ -38,8 +38,6 @@ const (
        Streebog512 = "streebog512"
 
        BLAKE2bMerkle     = "blake2b-merkle"
-       SHAKE128Merkle    = "shake128-merkle"
-       SHAKE256Merkle    = "shake256-merkle"
        Streebog256Merkle = "streebog256-merkle"
        Streebog512Merkle = "streebog512-merkle"
 )
@@ -75,12 +73,12 @@ func ByName(name string) hash.Hash {
                return h
        case SHAKE128:
                return NewSHAKE128()
-       case SHAKE256:
+       case SHAKE256, SPHINCSPlusSHAKE256sNonRandom, SPHINCSPlusSHAKE256sNonRandomPh:
                return NewSHAKE256()
        case SHAKE128Merkle:
                return NewSHAKE128MerkleHasher(
                        merkle.DefaultChunkLen, DefaultNumCPU)
-       case SHAKE256Merkle:
+       case SHAKE256Merkle, SPHINCSPlusSHAKE256sNonRandomMerkle:
                return NewSHAKE256MerkleHasher(
                        merkle.DefaultChunkLen, DefaultNumCPU)
        }
index 78157b8baf528f82a66ed15dcf794a3b558dd750005367d29fa9ce147b695599..4de9849a1e1d233d8c092bb21c0bdbb63fd177378c3c61cabc21150dc8f88825 100644 (file)
@@ -23,6 +23,15 @@ import (
        "go.cypherpunks.su/keks/cm/hash/merkle"
 )
 
+const (
+       SHAKE128Merkle = "shake128-merkle"
+       SHAKE256Merkle = "shake256-merkle"
+
+       SPHINCSPlusSHAKE256sNonRandom       = "sphincs+-shake-256s-nonrandom"
+       SPHINCSPlusSHAKE256sNonRandomPh     = "sphincs+-shake-256s-nonrandom-ph"
+       SPHINCSPlusSHAKE256sNonRandomMerkle = "sphincs+-shake-256s-nonrandom-merkle"
+)
+
 type SHAKE struct {
        xof *sha3.SHAKE
        l   int
index eb0a2997995a19da392e65d88ace9ba30e32b9c0d619bd1fb5aec3c782883f56..7c7cdbd142005ddbfcf6f49b8ec68bef20debfc06a966ffcf4f3c6ce6eb8fb39 100644 (file)
@@ -23,6 +23,7 @@ import (
        "go.cypherpunks.su/keks/cm"
        ed25519blake2b "go.cypherpunks.su/keks/cm/sign/ed25519-blake2b"
        "go.cypherpunks.su/keks/cm/sign/gost"
+       "go.cypherpunks.su/keks/cm/sign/spx"
        "go.cypherpunks.su/keks/schema"
 )
 
@@ -59,6 +60,8 @@ func PrvParse(data []byte) (prv Iface, pub []byte, err error) {
                prv, pub, err = ed25519blake2b.NewSigner(av.V)
        case gost.GOST3410256A, gost.GOST3410512C:
                prv, pub, err = gost.NewSigner(av.V)
+       case spx.SPHINCSPlusSHAKE256s:
+               prv, pub, err = spx.NewSigner(av.V)
        default:
                err = fmt.Errorf("unknown private key algo: %s", av.A)
        }
index 6df41dac0ec352d24c080ab83e764f1c649090672d67ac6a200c9076754d9288..88a1972e0e2cdc145c6a4dc7e3c77ac5672e7ce5b016f041b8789b4494963a56 100644 (file)
@@ -28,6 +28,7 @@ import (
        "go.cypherpunks.su/keks/cm"
        ed25519blake2b "go.cypherpunks.su/keks/cm/sign/ed25519-blake2b"
        "go.cypherpunks.su/keks/cm/sign/gost"
+       "go.cypherpunks.su/keks/cm/sign/spx"
        "go.cypherpunks.su/keks/schema"
 )
 
@@ -139,6 +140,14 @@ func (pub *PubLoad) CheckSignature(algo string, signed, signature []byte) (err e
                if !valid {
                        err = ErrSigInvalid
                }
+       case spx.SPHINCSPlusSHAKE256s:
+               if algo != spx.SPHINCSPlusSHAKE256sNonRandom {
+                       return ErrBadSigAlgo
+               }
+               valid, err = spx.Verify(key.A, key.V, signed, signature)
+               if !valid {
+                       err = ErrSigInvalid
+               }
        default:
                err = errors.New("unsupported signature algorithm")
        }
@@ -182,6 +191,17 @@ func (pub *PubLoad) CheckSignaturePrehash(
                if !valid {
                        err = ErrSigInvalid
                }
+       case spx.SPHINCSPlusSHAKE256s:
+               switch algo {
+               case spx.SPHINCSPlusSHAKE256sNonRandomPh:
+               case spx.SPHINCSPlusSHAKE256sNonRandomMerkle:
+               default:
+                       return ErrBadSigAlgo
+               }
+               valid, err = spx.VerifyPrehash(key.A, key.V, prehash, signature)
+               if !valid {
+                       err = ErrSigInvalid
+               }
        default:
                err = errors.New("unsupported signature algorithm")
        }
diff --git a/go/cm/sign/spx/kp.go b/go/cm/sign/spx/kp.go
new file mode 100644 (file)
index 0000000..9b7b2c1
--- /dev/null
@@ -0,0 +1,45 @@
+// 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 spx
+
+import (
+       spxParams "github.com/kasperdi/SPHINCSPLUS-golang/parameters"
+       spx "github.com/kasperdi/SPHINCSPLUS-golang/sphincs"
+)
+
+const (
+       SPHINCSPlusSHAKE256s                = "sphincs+-shake-256s"
+       SPHINCSPlusSHAKE256sNonRandom       = "sphincs+-shake-256s-nonrandom"
+       SPHINCSPlusSHAKE256sNonRandomPh     = "sphincs+-shake-256s-nonrandom-ph"
+       SPHINCSPlusSHAKE256sNonRandomMerkle = "sphincs+-shake-256s-nonrandom-merkle"
+)
+
+var Params = spxParams.MakeSphincsPlusSHAKE256256sRobust(false)
+
+func NewKeypair(algo string) (signer *Signer, prv, pub []byte, err error) {
+       sk, pk := spx.Spx_keygen(Params)
+       pub, err = pk.SerializePK()
+       if err != nil {
+               return
+       }
+       prv, err = sk.SerializeSK()
+       if err != nil {
+               return
+       }
+       prv = append(prv, pub...)
+       signer, _, err = NewSigner(prv)
+       return
+}
diff --git a/go/cm/sign/spx/signer.go b/go/cm/sign/spx/signer.go
new file mode 100644 (file)
index 0000000..5531a79
--- /dev/null
@@ -0,0 +1,105 @@
+// 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 spx
+
+import (
+       "crypto"
+       "errors"
+       "hash"
+       "io"
+
+       spx "github.com/kasperdi/SPHINCSPLUS-golang/sphincs"
+
+       cmhash "go.cypherpunks.su/keks/cm/hash"
+       "go.cypherpunks.su/keks/cm/hash/merkle"
+       "go.cypherpunks.su/keks/cm/sign/mode"
+)
+
+type Signer struct {
+       NewHasher func() hash.Hash
+       SK        *spx.SPHINCS_SK
+       PK        *spx.SPHINCS_PK
+       prehasher *hash.Hash
+       mode      mode.Mode
+}
+
+func (s *Signer) SetMode(m mode.Mode) error {
+       switch m {
+       case mode.Pure:
+               s.mode = m
+               return nil
+       case mode.Prehash:
+               s.mode = m
+               p := cmhash.NewSHAKE256()
+               s.prehasher = &p
+               return nil
+       case mode.Merkle:
+               s.mode = m
+               p := cmhash.NewSHAKE256MerkleHasher(merkle.DefaultChunkLen, cmhash.DefaultNumCPU)
+               s.prehasher = &p
+               return nil
+       default:
+               return errors.New("unsupported mode")
+       }
+}
+
+func (s *Signer) Mode() mode.Mode {
+       return s.mode
+}
+
+func (s *Signer) Prehasher() *hash.Hash {
+       return s.prehasher
+}
+
+func (s *Signer) Algo() string {
+       switch s.mode {
+       case mode.Pure:
+               return SPHINCSPlusSHAKE256sNonRandom
+       case mode.Prehash:
+               return SPHINCSPlusSHAKE256sNonRandomPh
+       case mode.Merkle:
+               return SPHINCSPlusSHAKE256sNonRandomMerkle
+       }
+       return ""
+}
+
+func (s *Signer) Public() crypto.PublicKey {
+       return s.PK
+}
+
+func (s *Signer) Sign(
+       rand io.Reader,
+       msg []byte,
+       opts crypto.SignerOpts,
+) (signature []byte, err error) {
+       sig := spx.Spx_sign(Params, msg, s.SK)
+       return sig.SerializeSignature()
+}
+
+func NewSigner(v []byte) (prv *Signer, pub []byte, err error) {
+       signer := Signer{}
+       signer.SK, err = spx.DeserializeSK(Params, v[:128])
+       if err != nil {
+               return
+       }
+       signer.PK, err = spx.DeserializePK(Params, v[128:])
+       if err != nil {
+               return
+       }
+       prv = &signer
+       pub, err = signer.PK.SerializePK()
+       return
+}
diff --git a/go/cm/sign/spx/verify.go b/go/cm/sign/spx/verify.go
new file mode 100644 (file)
index 0000000..1391449
--- /dev/null
@@ -0,0 +1,39 @@
+// 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 spx
+
+import (
+       spx "github.com/kasperdi/SPHINCSPLUS-golang/sphincs"
+)
+
+func Verify(algo string, pub, signed, signature []byte) (valid bool, err error) {
+       var pk *spx.SPHINCS_PK
+       pk, err = spx.DeserializePK(Params, pub)
+       if err != nil {
+               return
+       }
+       var sig *spx.SPHINCS_SIG
+       sig, err = spx.DeserializeSignature(Params, signature)
+       if err != nil {
+               return
+       }
+       valid = spx.Spx_verify(Params, signed, sig, pk)
+       return
+}
+
+func VerifyPrehash(algo string, pub, hsh, signature []byte) (valid bool, err error) {
+       return Verify(algo, pub, hsh, signature)
+}
index 876977f03d7af8d88cf14b58bb08f12c6dc37d7eafebc8652a7a5d089ff4b649..d00294a1b56a7dad0538fae3a03c27bb87686961638dacbb5b347d5cc836ee4d 100644 (file)
@@ -50,3 +50,15 @@ Stored in a file, it should begin with "cm/prv" @ref{MAGIC, magic}.
     and X25519's 32-byte one.
 
     @code{mceliece6960119-x25519} algorithm identifier is used.
+
+@node cm-prv-sphincs+-shake-256s
+@cindex cm-prv-sphincs+-shake-256s
+@nodedescription cm/prv with SPHINCS+-SHAKE256-256s-robust
+@subsection cm/prv with SPHINCS+-SHAKE256-256s-robust
+
+    @url{https://sphincs.org/, SPHINCS+} with
+    @url{https://keccak.team/, SHAKE256} hash,
+    255-bit security level, small signatures and robust parameters.
+    Value is concatenation of private and public keys (128+64 bytes).
+
+    Algorithm identifier for the public key: @code{sphincs+-shake-256s}.
index a9bdfaeba92d44bb1fcc7fc2eb91a49da46eefbae28849735fae532b41286165..f922522d6de2778352c38f360fbe1216458a84732d16b1b970f2da6114d51ef1 100644 (file)
@@ -190,3 +190,16 @@ MAP {
     of the concatenated public keys in @code{/load/v/pub/0}, that could
     save resources during @ref{kem-mceliece6960119-x25519-hkdf-shake256}
     KDF calculations.
+
+@node cm-pub-sphincs+-shake-256s
+@cindex cm-pub-sphincs+-shake-256s
+@nodedescription cm/pub with SPHINCS+-SHAKE256-256s-robust
+@subsection cm/pub with SPHINCS+-SHAKE256-256s-robust
+
+    @url{https://sphincs.org/, SPHINCS+} with
+    @url{https://keccak.team/, SHAKE256} hash,
+    255-bit security level, small signatures and robust parameters.
+
+    @code{sphincs+-shake-256s} algorithm identifier is used.
+
+    Public key's fingerprint should be calculated using SHAKE128.
index c5da863c0e46cb11a9d967ca569b39c540725eec6dd3586b4b2e087efcfef4b4..4252f8fcbc660585c5f25b044c0f75ee3082f5a3c3dd3d7ddc7ffbc80f65bf5c 100644 (file)
@@ -115,3 +115,27 @@ recipient's public key fingerprint(s).
     @ref{cm-hashed-blake2b-merkle} Merkle-tree hashing is used.
     HashEdDSA mode is used with @code{ed25519ph-blake2b-merkle}
     algorithm identifier for signature.
+
+@node cm-signed-sphincs+-shake-256s-nonrandom
+@cindex cm-signed-sphincs+-shake-256s-nonrandom
+@nodedescription cm/signed with SPHINCS+-SHAKE256-256s-robust non-random
+@subsection cm/signed with SPHINCS+-SHAKE256-256s-robust non-random
+
+    @url{https://sphincs.org/, SPHINCS+} with
+    @url{https://keccak.team/, SHAKE256} hash,
+    255-bit security level, small signatures,
+    robust parameters and deterministic signatures.
+
+    @code{sphincs+-shake-256s-nonrandom} algorithm identifier
+    must be used for the signature in pure signing mode.
+    @code{sphincs+-shake-256s-nonrandom-ph} is used in prehash mode.
+
+@node cm-signed-sphincs+-shake-256s-nonrandom-merkle
+@cindex cm-signed-sphincs+-shake-256s-nonrandom-merkle
+@nodedescription cm-signed-sphincs+-shake-256s-nonrandom with Merkle-tree hashing
+@subsection cm-signed-sphincs+-shake-256s-nonrandom with Merkle-tree hashing
+
+    @ref{cm-hashed-shake-merkle, shake256-merkle} Merkle-tree hashing is used.
+
+    @code{sphincs+-shake-256s-nonrandom-merkle} algorithm
+    identifier must be used for the signature.