gmsm/slhdsa/wots.go

125 lines
3.7 KiB
Go
Raw Normal View History

2025-05-21 11:10:44 +08:00
// 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
// Chaining function used in WOTS, it takes an n-byte inout and integer start and steps as input
// and returns the result of iterating a hash function F on the inout steps times, starting from start.
//
2025-05-21 11:10:44 +08:00
// See FIPS 205 Algorithm 5 wots_chain
func (pk *PublicKey) wotsChain(inout []byte, start, steps byte, addr adrsOperations) {
for i := start; i < start+steps; i++ {
addr.setHashAddress(uint32(i))
pk.h.f(pk, addr, inout, inout)
}
}
// wotsPkGen generates a WOTS public key.
//
2025-05-21 11:10:44 +08:00
// See FIPS 205 Algorithm 6 wots_pkGen
func (sk *PrivateKey) wotsPkGen(out, tmpBuf []byte, addr adrsOperations) {
2025-05-21 11:10:44 +08:00
skADRS := sk.addressCreator()
skADRS.clone(addr)
skADRS.setTypeAndClear(AddressTypeWOTSPRF)
skADRS.copyKeyPairAddress(addr)
tmp := tmpBuf
// compute [len] public values
for i := range sk.params.len {
// compute secret value for chain i
2025-05-21 11:10:44 +08:00
skADRS.setChainAddress(i)
sk.h.prf(sk, skADRS, tmp)
// compute public value for chain i
2025-05-21 11:10:44 +08:00
addr.setChainAddress(i)
sk.wotsChain(tmp, 0, 15, addr) // w = 16
tmp = tmp[sk.params.n:]
}
wotspkADRS := sk.addressCreator()
wotspkADRS.clone(addr)
wotspkADRS.setTypeAndClear(AddressTypeWOTSPK)
wotspkADRS.copyKeyPairAddress(addr)
// compress public key
2025-05-21 11:10:44 +08:00
sk.h.t(&sk.PublicKey, wotspkADRS, tmpBuf, out)
}
// wotsSign generates a WOTS signature on an n-byte message.
//
2025-05-21 11:10:44 +08:00
// See FIPS 205 Algorithm 10 wots_sign
2025-05-22 15:47:56 +08:00
func (sk *PrivateKey) wotsSign(msg []byte, adrs adrsOperations, sigWots []byte) {
2025-06-19 16:37:53 +08:00
var msgAndCsum [maxWotsLen]byte
2025-05-21 11:10:44 +08:00
// convert message to base w=16
2025-05-22 15:47:56 +08:00
bytes2nibbles(msg, msgAndCsum[:])
2025-05-21 11:10:44 +08:00
// compute checksum
// checksum = 15 * len1 - sum(msgAndCsum)
var csum uint16
len1 := sk.params.n * 2
for i := range len1 {
csum += uint16(msgAndCsum[i])
}
csum = uint16(15*len1) - csum
msgAndCsum[len1] = byte(csum>>8) & 0x0F
msgAndCsum[len1+1] = byte(csum>>4) & 0x0F
msgAndCsum[len1+2] = byte(csum) & 0x0F
// copy address to create key generation key address
2025-05-21 11:10:44 +08:00
skADRS := sk.addressCreator()
skADRS.clone(adrs)
skADRS.setTypeAndClear(AddressTypeWOTSPRF)
skADRS.copyKeyPairAddress(adrs)
for i := range sk.params.len {
skADRS.setChainAddress(i)
// compute chain i secret value
2025-05-21 11:10:44 +08:00
sk.h.prf(sk, skADRS, sigWots)
adrs.setChainAddress(i)
// compute chain i signature value
2025-05-21 11:10:44 +08:00
sk.wotsChain(sigWots, 0, msgAndCsum[i], adrs)
sigWots = sigWots[sk.params.n:]
}
}
// wotsPkFromSig computes a WOTS public key from a message and its signature
//
2025-05-21 11:10:44 +08:00
// See FIPS 205 Algorithm 8 wots_pkFromSig
2025-05-22 15:47:56 +08:00
func (pk *PublicKey) wotsPkFromSig(signature, msg, tmpBuf []byte, adrs adrsOperations, out []byte) {
2025-06-19 16:37:53 +08:00
var msgAndCsum [maxWotsLen]byte
2025-05-21 11:10:44 +08:00
// convert message to base w=16
2025-05-22 15:47:56 +08:00
bytes2nibbles(msg, msgAndCsum[:])
2025-05-21 11:10:44 +08:00
// compute checksum
// checksum = 15 * len1 - sum(msgAndCsum)
var csum uint16
len1 := pk.params.n * 2
for i := range len1 {
csum += uint16(msgAndCsum[i])
}
csum = uint16(15*len1) - csum
// convert checksum to base w=16 (left shift by 4 first)
msgAndCsum[len1] = byte(csum>>8) & 0x0F
msgAndCsum[len1+1] = byte(csum>>4) & 0x0F
msgAndCsum[len1+2] = byte(csum) & 0x0F
copy(tmpBuf, signature)
tmp := tmpBuf
for i := range pk.params.len {
adrs.setChainAddress(i)
pk.wotsChain(tmp, msgAndCsum[i], 15-msgAndCsum[i], adrs)
tmp = tmp[pk.params.n:]
}
// copy address to create WOTS+ public key address
2025-05-21 11:10:44 +08:00
wotspkADRS := pk.addressCreator()
wotspkADRS.clone(adrs)
wotspkADRS.setTypeAndClear(AddressTypeWOTSPK)
wotspkADRS.copyKeyPairAddress(adrs)
// compress public key
2025-05-21 11:10:44 +08:00
pk.h.t(pk, wotspkADRS, tmpBuf, out)
}
func bytes2nibbles(in, out []byte) {
for i := range in {
out[i*2] = in[i] >> 4
out[i*2+1] = in[i] & 0x0F
}
}