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 \
"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 (
gost.GOST3410512C,
sntrup4591761x25519.SNTRUP4591761X25519,
mceliece6960119x25519.ClassicMcEliece6960119X25519,
+ spx.SPHINCSPlusSHAKE256s,
}
sort.Strings(algos)
for _, s := range algos {
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")
}
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")
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"
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
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=
Streebog512 = "streebog512"
BLAKE2bMerkle = "blake2b-merkle"
- SHAKE128Merkle = "shake128-merkle"
- SHAKE256Merkle = "shake256-merkle"
Streebog256Merkle = "streebog256-merkle"
Streebog512Merkle = "streebog512-merkle"
)
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)
}
"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
"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"
)
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)
}
"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"
)
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")
}
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")
}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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)
+}
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}.
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.
@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.