gmsm/slhdsa/dsa.go
github-actions[bot] 9e364cb1d8
Release v0.33.0
* build(deps): bump github/codeql-action from 3.29.11 to 3.30.0 (#361)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.11 to 3.30.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](3c3833e0f8...2d92b76c45)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump codecov/codecov-action from 5.5.0 to 5.5.1 (#362)

Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.5.0 to 5.5.1.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](fdcc847654...5a1091511a)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: 5.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump actions/setup-go from 5.5.0 to 6.0.0 (#363)

Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.5.0 to 6.0.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](d35c59abb0...4469467582)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github/codeql-action from 3.30.0 to 3.30.1 (#364)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.0 to 3.30.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](2d92b76c45...f1f6e5f6af)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump step-security/harden-runner from 2.13.0 to 2.13.1 (#367)

Bumps [step-security/harden-runner](https://github.com/step-security/harden-runner) from 2.13.0 to 2.13.1.
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](ec9f2d5744...f4a75cfd61)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-version: 2.13.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github/codeql-action from 3.30.1 to 3.30.2 (#368)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.1 to 3.30.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](f1f6e5f6af...d3678e237b)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(mlkem): initialize mlkem from golang standard library

* chore(mlkem): refactoring, reduce alloc times

* build(deps): bump github/codeql-action from 3.30.2 to 3.30.3 (#369)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.2 to 3.30.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](d3678e237b...192325c861)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* doc(README): include MLKEM

* mldsa: refactor the implementation of key and sign/verify

* mldsa,slhdsa: crypto.Signer assertion

* fix(slhdsa): GenerateKey slice issue #72

* fix(slhdsa): copy/paste issue

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sun Yimin <emmansun@users.noreply.github.com>
2025-09-15 11:52:26 +08:00

175 lines
5.4 KiB
Go

// Copyright 2025 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
//go:build go1.24
package slhdsa
import (
"crypto"
"errors"
"io"
)
var _ crypto.Signer = (*PrivateKey)(nil)
type Options struct {
Context []byte
AddRand []byte // optional randomness to be added to the signature. If nil, the signature is deterministic.
}
func (opts *Options) HashFunc() crypto.Hash {
return crypto.Hash(0)
}
// Sign produces a signature of the message using the private key.
// It is a wrapper around the SignMessage method, implementing the crypto.Signer interface.
func (sk *PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
return sk.SignMessage(rand, message, opts)
}
// Sign generates a pure SLH-DSA signature for the given message.
// The signature is deterministic if the addRand parameter is nil.
// If addRand is not nil, it must be of the same length as n.
//
// See FIPS 205 Algorithm 22 slh_sign
func (sk *PrivateKey) SignMessage(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
if len(message) == 0 {
return nil, errors.New("slhdsa: empty message")
}
var context, addRand []byte
if opts, ok := opts.(*Options); ok {
context = opts.Context
addRand = opts.AddRand
}
if len(addRand) > 0 && len(addRand) != int(sk.params.n) {
return nil, errors.New("slhdsa: addrnd should be nil (deterministic variant) or of length n")
}
ctxLen := len(context)
if ctxLen > maxContextLen {
return nil, errors.New("slhdsa: context too long")
}
var mPrefix [maxContextLen + 2]byte
mPrefix[1] = byte(ctxLen)
if ctxLen > 0 {
copy(mPrefix[2:], context)
}
return sk.signInternal(mPrefix[:2+ctxLen], message, addRand)
}
// See FIPS 205 Algorithm 19 slh_sign_internal
func (sk *PrivateKey) signInternal(msgPrefix, message, addRand []byte) ([]byte, error) {
signatureHead := make([]byte, sk.params.sigLen)
// generate randomizer
if len(addRand) == 0 {
// substitute addRand with sk.PublicKey.seed for the deterministic variant
addRand = sk.PublicKey.seed[:sk.params.n]
}
sk.h.prfMsg(sk, addRand, msgPrefix, message, signatureHead)
R := signatureHead[:sk.params.n]
signature := signatureHead[sk.params.n:]
// compute message digest
var digest [maxM]byte
sk.h.hMsg(&sk.PublicKey, R, msgPrefix, message, digest[:])
// Grab the first mdLen() bytes of digest to use in fors_sign()
mdLen := sk.params.mdLen()
md := digest[:mdLen]
// Grab remaining bytes from digest to select tree and leaf id's
remaining := digest[mdLen:]
treeIdxLen := sk.params.treeIdxLen()
leafIdxLen := sk.params.leafIdxLen()
treeIdx := toInt(remaining[:treeIdxLen]) & sk.params.treeIdxMask()
remaining = remaining[treeIdxLen:]
leafIdx := uint32(toInt(remaining[:leafIdxLen]) & sk.params.leafIdxMask())
// The address adrs must have the layer address set to zero (since the XMSS tree that signs a FORS key is always at layer 0),
// the tree address set to the index of the WOTS+ key within the XMSS tree that signs the FORS key.
adrs := sk.addressCreator()
adrs.setTreeAddress(treeIdx)
adrs.setTypeAndClear(AddressTypeFORSTree)
adrs.setKeyPairAddress(leafIdx)
// generate the FORS signature and append it to the SLH-DSA signature
sk.forsSign(md, adrs, signature)
var pkFors [maxN]byte
// calculate the FORS public key using the generated FORS signature
signature = sk.forsPkFromSig(md, signature, adrs, pkFors[:])
// generate ht signature and append to the SLH-DSA signature
sk.htSign(pkFors[:sk.params.n], treeIdx, leafIdx, signature)
return signatureHead, nil
}
// Verify verifies a pure SLH-DSA signature for the given message.
//
// See FIPS 205 Algorithm 24 slh_verify
func (pk *PublicKey) VerifyWithOptions(signature, message []byte, opts crypto.SignerOpts) bool {
if len(message) == 0 {
return false
}
var context []byte
if opts, ok := opts.(*Options); ok {
context = opts.Context
}
if len(context) > maxContextLen {
return false
}
ctxLen := len(context)
var msgPrefix [maxContextLen + 2]byte
msgPrefix[1] = byte(ctxLen)
if ctxLen > 0 {
copy(msgPrefix[2:], context)
}
return pk.verifyInternal(signature, msgPrefix[:2+ctxLen], message)
}
// See FIPS 205 Algorithm 20 slh_verify_internal
func (pk *PublicKey) verifyInternal(signature []byte, msgPrefix []byte, message []byte) bool {
if len(signature) != pk.params.sigLen {
return false
}
adrs := pk.addressCreator()
R := signature[:pk.params.n]
signature = signature[pk.params.n:]
// compute message digest
var digest [maxM]byte
pk.h.hMsg(pk, R, msgPrefix, message, digest[:])
// Grab the first mdLen() bytes of digest to use in fors_sign()
mdLen := pk.params.mdLen()
md := digest[:mdLen]
// Grab remaining bytes from digest to select tree and leaf id's
remaining := digest[mdLen:]
treeIdxLen := pk.params.treeIdxLen()
leafIdxLen := pk.params.leafIdxLen()
treeIdx := toInt(remaining[:treeIdxLen]) & pk.params.treeIdxMask()
remaining = remaining[treeIdxLen:]
leafIdx := uint32(toInt(remaining[:leafIdxLen]) & pk.params.leafIdxMask())
adrs.setTreeAddress(treeIdx)
adrs.setTypeAndClear(AddressTypeFORSTree)
adrs.setKeyPairAddress(leafIdx)
var pkFors [maxN]byte
// calculate the FORS public key using the given FORS signature
signature = pk.forsPkFromSig(md, signature, adrs, pkFors[:])
return pk.htVerify(pkFors[:pk.params.n], signature, treeIdx, leafIdx)
}
func toInt(b []byte) uint64 {
var ret uint64
for i := range b {
ret = ret<<8 + uint64(b[i])
}
return ret
}