mirror of
https://github.com/luxfi/accel.git
synced 2026-10-30 17:03:45 +00:00
436 lines
9.8 KiB
Go
436 lines
9.8 KiB
Go
//go:build accel
|
|
|
|
package lattice
|
|
|
|
import (
|
|
"github.com/luxfi/accel"
|
|
)
|
|
|
|
// GPU-accelerated lattice cryptography implementations.
|
|
// These dispatch to the accel library's GPU backends (CUDA, Metal, Vulkan)
|
|
// for parallel NTT, polynomial arithmetic, and sampling operations.
|
|
|
|
// computeNTTRoots precomputes roots of unity for NTT
|
|
func computeNTTRoots(params NTTParams) []uint64 {
|
|
n := int(params.N)
|
|
roots := make([]uint64, n)
|
|
roots[0] = 1
|
|
for i := 1; i < n; i++ {
|
|
roots[i] = mulMod(roots[i-1], params.Root, params.Modulus)
|
|
}
|
|
return roots
|
|
}
|
|
|
|
// computeINTTRoots precomputes inverse roots for INTT
|
|
func computeINTTRoots(params NTTParams) []uint64 {
|
|
n := int(params.N)
|
|
invRoot := modInverse(params.Root, params.Modulus)
|
|
roots := make([]uint64, n)
|
|
roots[0] = 1
|
|
for i := 1; i < n; i++ {
|
|
roots[i] = mulMod(roots[i-1], invRoot, params.Modulus)
|
|
}
|
|
return roots
|
|
}
|
|
|
|
func nttForwardGPU(sess *accel.Session, params NTTParams, coeffs []uint64) ([]uint64, error) {
|
|
n := int(params.N)
|
|
|
|
// Create input tensor
|
|
inputTensor, err := accel.NewTensorWithData[uint64](sess, []int{n}, coeffs)
|
|
if err != nil {
|
|
return nttForwardCPU(params, coeffs)
|
|
}
|
|
defer inputTensor.Close()
|
|
|
|
// Create output tensor
|
|
outputTensor, err := accel.NewTensor[uint64](sess, []int{n})
|
|
if err != nil {
|
|
return nttForwardCPU(params, coeffs)
|
|
}
|
|
defer outputTensor.Close()
|
|
|
|
// Precompute roots of unity
|
|
roots := computeNTTRoots(params)
|
|
rootsTensor, err := accel.NewTensorWithData[uint64](sess, []int{n}, roots)
|
|
if err != nil {
|
|
return nttForwardCPU(params, coeffs)
|
|
}
|
|
defer rootsTensor.Close()
|
|
|
|
// Dispatch NTT kernel via ZK ops
|
|
err = sess.ZK().NTT(
|
|
inputTensor.Untyped(),
|
|
outputTensor.Untyped(),
|
|
rootsTensor.Untyped(),
|
|
params.Modulus,
|
|
)
|
|
if err != nil {
|
|
return nttForwardCPU(params, coeffs)
|
|
}
|
|
|
|
// Sync
|
|
if err := sess.Sync(); err != nil {
|
|
return nttForwardCPU(params, coeffs)
|
|
}
|
|
|
|
// Read results
|
|
result, err := outputTensor.ToSlice()
|
|
if err != nil {
|
|
return nttForwardCPU(params, coeffs)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func nttInverseGPU(sess *accel.Session, params NTTParams, evals []uint64) ([]uint64, error) {
|
|
n := int(params.N)
|
|
|
|
// Create input tensor
|
|
inputTensor, err := accel.NewTensorWithData[uint64](sess, []int{n}, evals)
|
|
if err != nil {
|
|
return nttInverseCPU(params, evals)
|
|
}
|
|
defer inputTensor.Close()
|
|
|
|
// Create output tensor
|
|
outputTensor, err := accel.NewTensor[uint64](sess, []int{n})
|
|
if err != nil {
|
|
return nttInverseCPU(params, evals)
|
|
}
|
|
defer outputTensor.Close()
|
|
|
|
// Precompute inverse roots
|
|
invRoots := computeINTTRoots(params)
|
|
invRootsTensor, err := accel.NewTensorWithData[uint64](sess, []int{n}, invRoots)
|
|
if err != nil {
|
|
return nttInverseCPU(params, evals)
|
|
}
|
|
defer invRootsTensor.Close()
|
|
|
|
// Dispatch INTT kernel via ZK ops
|
|
err = sess.ZK().INTT(
|
|
inputTensor.Untyped(),
|
|
outputTensor.Untyped(),
|
|
invRootsTensor.Untyped(),
|
|
params.Modulus,
|
|
)
|
|
if err != nil {
|
|
return nttInverseCPU(params, evals)
|
|
}
|
|
|
|
// Sync
|
|
if err := sess.Sync(); err != nil {
|
|
return nttInverseCPU(params, evals)
|
|
}
|
|
|
|
// Read results
|
|
result, err := outputTensor.ToSlice()
|
|
if err != nil {
|
|
return nttInverseCPU(params, evals)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func polyMulGPU(sess *accel.Session, params NTTParams, a, b []uint64) ([]uint64, error) {
|
|
n := int(params.N)
|
|
|
|
// Create tensors
|
|
aTensor, err := accel.NewTensorWithData[uint64](sess, []int{n}, a)
|
|
if err != nil {
|
|
return polyMulCPU(params, a, b)
|
|
}
|
|
defer aTensor.Close()
|
|
|
|
bTensor, err := accel.NewTensorWithData[uint64](sess, []int{n}, b)
|
|
if err != nil {
|
|
return polyMulCPU(params, a, b)
|
|
}
|
|
defer bTensor.Close()
|
|
|
|
cTensor, err := accel.NewTensor[uint64](sess, []int{n})
|
|
if err != nil {
|
|
return polyMulCPU(params, a, b)
|
|
}
|
|
defer cTensor.Close()
|
|
|
|
// Dispatch polynomial multiplication kernel
|
|
// This uses NTT internally: c = INTT(NTT(a) * NTT(b))
|
|
err = sess.ZK().PolyMul(
|
|
aTensor.Untyped(),
|
|
bTensor.Untyped(),
|
|
cTensor.Untyped(),
|
|
params.Modulus,
|
|
)
|
|
if err != nil {
|
|
return polyMulCPU(params, a, b)
|
|
}
|
|
|
|
if err := sess.Sync(); err != nil {
|
|
return polyMulCPU(params, a, b)
|
|
}
|
|
|
|
result, err := cTensor.ToSlice()
|
|
if err != nil {
|
|
return polyMulCPU(params, a, b)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func polyAddGPU(sess *accel.Session, modulus uint64, a, b []uint64) ([]uint64, error) {
|
|
n := len(a)
|
|
|
|
// Create tensors
|
|
aTensor, err := accel.NewTensorWithData[uint64](sess, []int{n}, a)
|
|
if err != nil {
|
|
return polyAddCPU(modulus, a, b)
|
|
}
|
|
defer aTensor.Close()
|
|
|
|
bTensor, err := accel.NewTensorWithData[uint64](sess, []int{n}, b)
|
|
if err != nil {
|
|
return polyAddCPU(modulus, a, b)
|
|
}
|
|
defer bTensor.Close()
|
|
|
|
cTensor, err := accel.NewTensor[uint64](sess, []int{n})
|
|
if err != nil {
|
|
return polyAddCPU(modulus, a, b)
|
|
}
|
|
defer cTensor.Close()
|
|
|
|
// Dispatch field add kernel
|
|
err = sess.ZK().FieldAdd(
|
|
aTensor.Untyped(),
|
|
bTensor.Untyped(),
|
|
cTensor.Untyped(),
|
|
modulus,
|
|
)
|
|
if err != nil {
|
|
return polyAddCPU(modulus, a, b)
|
|
}
|
|
|
|
if err := sess.Sync(); err != nil {
|
|
return polyAddCPU(modulus, a, b)
|
|
}
|
|
|
|
result, err := cTensor.ToSlice()
|
|
if err != nil {
|
|
return polyAddCPU(modulus, a, b)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func polySubGPU(sess *accel.Session, modulus uint64, a, b []uint64) ([]uint64, error) {
|
|
n := len(a)
|
|
|
|
// Create tensors
|
|
aTensor, err := accel.NewTensorWithData[uint64](sess, []int{n}, a)
|
|
if err != nil {
|
|
return polySubCPU(modulus, a, b)
|
|
}
|
|
defer aTensor.Close()
|
|
|
|
// Compute -b = modulus - b for each element
|
|
negB := make([]uint64, n)
|
|
for i := range b {
|
|
if b[i] == 0 {
|
|
negB[i] = 0
|
|
} else {
|
|
negB[i] = modulus - b[i]
|
|
}
|
|
}
|
|
|
|
negBTensor, err := accel.NewTensorWithData[uint64](sess, []int{n}, negB)
|
|
if err != nil {
|
|
return polySubCPU(modulus, a, b)
|
|
}
|
|
defer negBTensor.Close()
|
|
|
|
cTensor, err := accel.NewTensor[uint64](sess, []int{n})
|
|
if err != nil {
|
|
return polySubCPU(modulus, a, b)
|
|
}
|
|
defer cTensor.Close()
|
|
|
|
// a + (-b) = a - b
|
|
err = sess.ZK().FieldAdd(
|
|
aTensor.Untyped(),
|
|
negBTensor.Untyped(),
|
|
cTensor.Untyped(),
|
|
modulus,
|
|
)
|
|
if err != nil {
|
|
return polySubCPU(modulus, a, b)
|
|
}
|
|
|
|
if err := sess.Sync(); err != nil {
|
|
return polySubCPU(modulus, a, b)
|
|
}
|
|
|
|
result, err := cTensor.ToSlice()
|
|
if err != nil {
|
|
return polySubCPU(modulus, a, b)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func batchNTTForwardGPU(sess *accel.Session, params NTTParams, polys [][]uint64) ([][]uint64, error) {
|
|
m := len(polys)
|
|
n := int(params.N)
|
|
|
|
// Precompute roots
|
|
roots := computeNTTRoots(params)
|
|
rootsTensor, err := accel.NewTensorWithData[uint64](sess, []int{n}, roots)
|
|
if err != nil {
|
|
return batchNTTForwardCPU(params, polys)
|
|
}
|
|
defer rootsTensor.Close()
|
|
|
|
// Process each polynomial
|
|
results := make([][]uint64, m)
|
|
for i := 0; i < m; i++ {
|
|
polyInput, err := accel.NewTensorWithData[uint64](sess, []int{n}, polys[i])
|
|
if err != nil {
|
|
return batchNTTForwardCPU(params, polys)
|
|
}
|
|
|
|
polyOutput, err := accel.NewTensor[uint64](sess, []int{n})
|
|
if err != nil {
|
|
polyInput.Close()
|
|
return batchNTTForwardCPU(params, polys)
|
|
}
|
|
|
|
err = sess.ZK().NTT(
|
|
polyInput.Untyped(),
|
|
polyOutput.Untyped(),
|
|
rootsTensor.Untyped(),
|
|
params.Modulus,
|
|
)
|
|
|
|
if err != nil {
|
|
polyInput.Close()
|
|
polyOutput.Close()
|
|
return batchNTTForwardCPU(params, polys)
|
|
}
|
|
|
|
if err := sess.Sync(); err != nil {
|
|
polyInput.Close()
|
|
polyOutput.Close()
|
|
return batchNTTForwardCPU(params, polys)
|
|
}
|
|
|
|
result, err := polyOutput.ToSlice()
|
|
if err != nil {
|
|
polyInput.Close()
|
|
polyOutput.Close()
|
|
return batchNTTForwardCPU(params, polys)
|
|
}
|
|
|
|
results[i] = result
|
|
polyInput.Close()
|
|
polyOutput.Close()
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
func batchNTTInverseGPU(sess *accel.Session, params NTTParams, polys [][]uint64) ([][]uint64, error) {
|
|
m := len(polys)
|
|
n := int(params.N)
|
|
|
|
// Precompute inverse roots
|
|
invRoots := computeINTTRoots(params)
|
|
invRootsTensor, err := accel.NewTensorWithData[uint64](sess, []int{n}, invRoots)
|
|
if err != nil {
|
|
return batchNTTInverseCPU(params, polys)
|
|
}
|
|
defer invRootsTensor.Close()
|
|
|
|
// Process each polynomial
|
|
results := make([][]uint64, m)
|
|
for i := 0; i < m; i++ {
|
|
polyInput, err := accel.NewTensorWithData[uint64](sess, []int{n}, polys[i])
|
|
if err != nil {
|
|
return batchNTTInverseCPU(params, polys)
|
|
}
|
|
|
|
polyOutput, err := accel.NewTensor[uint64](sess, []int{n})
|
|
if err != nil {
|
|
polyInput.Close()
|
|
return batchNTTInverseCPU(params, polys)
|
|
}
|
|
|
|
err = sess.ZK().INTT(
|
|
polyInput.Untyped(),
|
|
polyOutput.Untyped(),
|
|
invRootsTensor.Untyped(),
|
|
params.Modulus,
|
|
)
|
|
|
|
if err != nil {
|
|
polyInput.Close()
|
|
polyOutput.Close()
|
|
return batchNTTInverseCPU(params, polys)
|
|
}
|
|
|
|
if err := sess.Sync(); err != nil {
|
|
polyInput.Close()
|
|
polyOutput.Close()
|
|
return batchNTTInverseCPU(params, polys)
|
|
}
|
|
|
|
result, err := polyOutput.ToSlice()
|
|
if err != nil {
|
|
polyInput.Close()
|
|
polyOutput.Close()
|
|
return batchNTTInverseCPU(params, polys)
|
|
}
|
|
|
|
results[i] = result
|
|
polyInput.Close()
|
|
polyOutput.Close()
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
func sampleNTTGPU(sess *accel.Session, params NTTParams, seed []byte) ([]uint64, error) {
|
|
n := int(params.N)
|
|
|
|
// Create seed tensor
|
|
seedTensor, err := accel.NewTensorWithData[uint8](sess, []int{len(seed)}, seed)
|
|
if err != nil {
|
|
return sampleNTTCPU(params, seed)
|
|
}
|
|
defer seedTensor.Close()
|
|
|
|
// Create output tensor for hash expansion
|
|
// We need n * 32 bytes for n hash outputs
|
|
hashOutputTensor, err := accel.NewTensor[uint8](sess, []int{n, 32})
|
|
if err != nil {
|
|
return sampleNTTCPU(params, seed)
|
|
}
|
|
defer hashOutputTensor.Close()
|
|
|
|
// Use crypto SHA256 for hash expansion
|
|
// Note: This is a simplified approach - real implementation would use
|
|
// a proper hash-to-field function
|
|
err = sess.Crypto().SHA256(seedTensor.Untyped(), hashOutputTensor.Untyped())
|
|
if err != nil {
|
|
return sampleNTTCPU(params, seed)
|
|
}
|
|
|
|
if err := sess.Sync(); err != nil {
|
|
return sampleNTTCPU(params, seed)
|
|
}
|
|
|
|
// For proper sampling with counter mode, fall back to CPU
|
|
// GPU acceleration is more beneficial for large polynomial operations
|
|
return sampleNTTCPU(params, seed)
|
|
}
|