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
|
|
|
|
|
2025-05-22 14:21:24 +08:00
|
|
|
// forsSign generates a FORS signature. It signs a k*a-bits message digest md.
|
|
|
|
// In addition, it takes PrivateKey.seed and PublicKey.seed from the sk and and an address as input.
|
|
|
|
// The sigFors is a FORS signature of size n*k*(a+1) as result.
|
|
|
|
//
|
2025-05-21 11:10:44 +08:00
|
|
|
// See FIPS 205 Algorithm 16 fors_sign
|
|
|
|
func (sk *PrivateKey) forsSign(md []byte, adrs adrsOperations, sigFors []byte) {
|
|
|
|
var indices [MAX_K]uint32
|
2025-05-22 14:21:24 +08:00
|
|
|
// split md into k a-bits values, eatch of which is interpreted as an integer between 0 and 2^a-1.
|
2025-05-21 11:10:44 +08:00
|
|
|
base2b(md, sk.params.a, indices[:sk.params.k])
|
|
|
|
|
|
|
|
twoPowerA := uint32(1 << sk.params.a)
|
|
|
|
var treeIDTimeTwoPowerA uint32
|
|
|
|
|
|
|
|
for treeID := range sk.params.k {
|
|
|
|
nodeID := indices[treeID]
|
|
|
|
sk.forsGenPrivateKey(nodeID+treeIDTimeTwoPowerA, adrs, sigFors)
|
|
|
|
sigFors = sigFors[sk.params.n:]
|
|
|
|
|
2025-05-22 14:21:24 +08:00
|
|
|
// compute auth path
|
2025-05-21 11:10:44 +08:00
|
|
|
treeOffset := treeIDTimeTwoPowerA
|
2025-05-22 14:21:24 +08:00
|
|
|
for j := range sk.params.a {
|
2025-05-21 11:10:44 +08:00
|
|
|
s := nodeID ^ 1
|
2025-05-22 14:21:24 +08:00
|
|
|
sk.forsNode(s+treeOffset, j, adrs, sigFors)
|
2025-05-21 11:10:44 +08:00
|
|
|
|
|
|
|
nodeID >>= 1
|
|
|
|
treeOffset >>= 1
|
|
|
|
sigFors = sigFors[sk.params.n:]
|
|
|
|
}
|
2025-05-22 14:21:24 +08:00
|
|
|
treeIDTimeTwoPowerA += twoPowerA // same as treeIDTimeTwoPowerA = treeID*twoPowerA
|
2025-05-21 11:10:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// forsPkFromSig computes a FORS public key from a FORS signature.
|
2025-05-22 14:21:24 +08:00
|
|
|
//
|
2025-05-21 11:10:44 +08:00
|
|
|
// See FIPS 205 Algorithm 17 fors_pkFromSig
|
|
|
|
func (pk *PublicKey) forsPkFromSig(md, signature []byte, adrs adrsOperations, out []byte) []byte {
|
|
|
|
var indices [MAX_K]uint32
|
|
|
|
base2b(md, pk.params.a, indices[:pk.params.k])
|
|
|
|
|
|
|
|
twoPowerA := uint32(1 << pk.params.a)
|
|
|
|
|
|
|
|
var treeIDTimeTwoPowerA uint32
|
|
|
|
root := make([]byte, pk.params.n*pk.params.k)
|
|
|
|
rootPt := root
|
|
|
|
for treeID := range pk.params.k {
|
|
|
|
// compute leaf
|
|
|
|
nodeID := indices[treeID]
|
|
|
|
treeIdx := nodeID + treeIDTimeTwoPowerA
|
|
|
|
adrs.setTreeHeight(0)
|
|
|
|
adrs.setTreeIndex(treeIdx)
|
|
|
|
pk.h.f(pk, adrs, signature, rootPt)
|
|
|
|
signature = signature[pk.params.n:]
|
|
|
|
|
|
|
|
// compute root from leaf and AUTH
|
|
|
|
for layer := range pk.params.a {
|
|
|
|
adrs.setTreeHeight(layer + 1)
|
|
|
|
if nodeID&1 == 0 {
|
|
|
|
treeIdx = treeIdx >> 1
|
|
|
|
adrs.setTreeIndex(treeIdx)
|
|
|
|
pk.h.h(pk, adrs, rootPt, signature, rootPt)
|
|
|
|
} else {
|
|
|
|
treeIdx = (treeIdx - 1) >> 1
|
|
|
|
adrs.setTreeIndex(treeIdx)
|
|
|
|
pk.h.h(pk, adrs, signature, rootPt, rootPt)
|
|
|
|
}
|
|
|
|
signature = signature[pk.params.n:]
|
|
|
|
nodeID >>= 1
|
|
|
|
}
|
|
|
|
treeIDTimeTwoPowerA += twoPowerA
|
|
|
|
rootPt = rootPt[pk.params.n:]
|
|
|
|
}
|
|
|
|
// copy address to create a FORS public-key address
|
|
|
|
forspkADRS := pk.addressCreator()
|
|
|
|
forspkADRS.clone(adrs)
|
|
|
|
forspkADRS.setTypeAndClear(AddressTypeFORSRoots)
|
|
|
|
forspkADRS.copyKeyPairAddress(adrs)
|
2025-05-22 14:21:24 +08:00
|
|
|
// compute FORS public key
|
2025-05-21 11:10:44 +08:00
|
|
|
pk.h.t(pk, forspkADRS, root, out)
|
|
|
|
return signature
|
|
|
|
}
|
|
|
|
|
|
|
|
// forsNode computes the root of a Merkle subtree of FORS public values.
|
2025-05-22 14:21:24 +08:00
|
|
|
//
|
2025-05-21 11:10:44 +08:00
|
|
|
// See FIPS 205 Algorithm 15 fors_node
|
|
|
|
func (sk *PrivateKey) forsNode(nodeID, layer uint32, adrs adrsOperations, out []byte) {
|
|
|
|
if layer == 0 {
|
2025-05-22 14:21:24 +08:00
|
|
|
// If the subtree consists of a signle leaf node, then it simply returns a hash of the node's
|
|
|
|
// private n-byte string.
|
2025-05-21 11:10:44 +08:00
|
|
|
sk.forsGenPrivateKey(nodeID, adrs, out)
|
|
|
|
adrs.setTreeHeight(0)
|
|
|
|
adrs.setTreeIndex(nodeID)
|
|
|
|
sk.h.f(&sk.PublicKey, adrs, out, out)
|
|
|
|
} else {
|
2025-05-22 14:21:24 +08:00
|
|
|
// otherwise, it computes the roots of the left subtree and right subtree
|
|
|
|
// and hashs them togeter.
|
2025-05-21 11:10:44 +08:00
|
|
|
var lnode, rnode [MAX_N]byte
|
|
|
|
sk.forsNode(nodeID*2, layer-1, adrs, lnode[:])
|
|
|
|
sk.forsNode(nodeID*2+1, layer-1, adrs, rnode[:])
|
|
|
|
adrs.setTreeHeight(layer)
|
|
|
|
adrs.setTreeIndex(nodeID)
|
|
|
|
sk.h.h(&sk.PublicKey, adrs, lnode[:], rnode[:], out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// forsGenPrivateKey generates a FORS private key value.
|
2025-05-22 14:21:24 +08:00
|
|
|
//
|
2025-05-21 11:10:44 +08:00
|
|
|
// See FIPS 205 Algorithm 14 fors_skGen
|
2025-05-22 14:21:24 +08:00
|
|
|
func (sk *PrivateKey) forsGenPrivateKey(idx uint32, adrs adrsOperations, out []byte) {
|
2025-05-21 11:10:44 +08:00
|
|
|
skADRS := sk.addressCreator()
|
|
|
|
skADRS.clone(adrs)
|
|
|
|
skADRS.setTypeAndClear(AddressTypeFORSPRF)
|
|
|
|
skADRS.copyKeyPairAddress(adrs)
|
2025-05-22 14:21:24 +08:00
|
|
|
skADRS.setTreeIndex(idx)
|
2025-05-21 11:10:44 +08:00
|
|
|
sk.h.prf(sk, skADRS, out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// base2b computes the base-2^b representation of the input byte array.
|
2025-05-22 14:21:24 +08:00
|
|
|
//
|
2025-05-21 11:10:44 +08:00
|
|
|
// See FIPS 205 Algorithm 4 base_2^b
|
|
|
|
func base2b(in []byte, base uint32, out []uint32) {
|
|
|
|
var (
|
|
|
|
bits uint32
|
|
|
|
total uint32
|
|
|
|
mask uint32
|
|
|
|
idx int
|
|
|
|
)
|
|
|
|
mask = 1<<base - 1
|
|
|
|
|
|
|
|
for i := range out {
|
|
|
|
for ; bits < base; bits += 8 {
|
|
|
|
total = (total << 8) | uint32(in[idx])
|
|
|
|
idx++
|
|
|
|
}
|
|
|
|
bits -= base
|
|
|
|
out[i] = (total >> bits) & mask
|
|
|
|
}
|
|
|
|
}
|