mirror of
https://github.com/emmansun/gmsm.git
synced 2025-06-03 01:44:54 +00:00
147 lines
4.6 KiB
Go
147 lines
4.6 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 (
|
|
"errors"
|
|
)
|
|
|
|
// 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) Sign(message, context, addRand []byte) ([]byte, error) {
|
|
if len(message) == 0 {
|
|
return nil, errors.New("slhdsa: empty message")
|
|
}
|
|
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 > MAX_CONTEXT_LEN {
|
|
return nil, errors.New("slhdsa: context too long")
|
|
}
|
|
|
|
var mPrefix [MAX_CONTEXT_LEN + 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 [MAX_M]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 [MAX_N]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) Verify(signature, message, context []byte) bool {
|
|
if len(message) == 0 {
|
|
return false
|
|
}
|
|
if len(context) > MAX_CONTEXT_LEN {
|
|
return false
|
|
}
|
|
|
|
ctxLen := len(context)
|
|
var msgPrefix [MAX_CONTEXT_LEN + 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 [MAX_M]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 [MAX_N]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
|
|
}
|