mirror of
https://github.com/luxfi/crypto.git
synced 2026-10-30 16:59:13 +00:00
Replace the gpu.go one-liner stub with a real, full accelerator that
exposes the FIPS 204 NTT primitive plus batched sign/verify across all
three parameter sets via one shared NTT kernel.
The package decomposes into:
params.go FIPS 204 parameter table (modes 2/3/5 → k, ℓ, η, τ,
β, γ₁, γ₂, ω, sizes). Single table; the NTT kernel
is mode-independent because every set shares the same
ring Z_q[X]/(X^256+1) with q=8380417.
ntt_cpu.go Pure-Go negacyclic NTT/INTT over the FIPS 204 ring.
Canonical Cooley-Tukey forward + Gentleman-Sande
inverse, FIPS 204 Appendix B zeta table. Constant-
time helpers (reduce32, addModQ, subModQ, mulModQ).
parallel.go Worker-pool helpers (capWorkers, parallelDo) shared
by both engines.
gpu.go Public dispatcher: Accelerator with threshold-routed
NTTForward/Inverse/Mul Batch, SignBatch, VerifyBatch,
Stats. Defaults: ≥16 polys for NTT GPU, ≥8 for sign,
≥16 for verify. Stats counters expose GPU vs CPU
dispatch.
gpu_cgo.go (build tag cgo) — luxfi/accel-backed engine. NTT/Mul
use accel.Lattice().PolynomialNTT/INTT/Mul; batched
sign/verify use MLDSASignBatch / MLDSAVerifyBatch with
mode ∈ {2, 3, 5}. Per-op GPU failures fall through to
a parallel CPU path so the dispatcher contract
("every batch completes") holds.
gpu_nocgo.go (build tag !cgo) — pure-Go engine. NTT goes to
ntt_cpu.go; sign/verify go to circl-backed
mldsa{44,65,87} wrappers. parallelDo fan-out matches
GOMAXPROCS so CPU-only hosts still get multi-core
throughput. No stubs.
Tests cover: FIPS 204 parameter cross-check vs circl, NTT∘INTT
roundtrip, linearity, known vector, schoolbook PolyMul reference,
constant-time helper bounds, threshold routing, batch sign+verify
across all three modes, cross-wrapper equivalence (accelerator
signatures verify under plain mldsa{44,65,87}.Verify).
Both CGO_ENABLED={0,1} build and test green. KAT vectors in
pq/mldsa/mldsa{44,65,87} unchanged.
Apple M1 Max numbers (single-poly NTT, batched sign/verify):
CPU NTTForward ~2.6-3.3µs/op
CPU PolyMul ~7-9µs/op
SignBatch 44 batch64 ~6.5ms (CPU) / ~8.1ms (CGO+accel)
SignBatch 65 batch64 ~6.8ms (CPU) / ~12.3ms
SignBatch 87 batch64 ~23ms (CPU) / ~45ms
VerifyBatch 44 batch64 ~5ms (CPU) / ~6.1ms
VerifyBatch 65 batch64 ~4.7ms (CPU) / ~7.6ms
VerifyBatch 87 batch64 ~7.5ms (CPU) / ~10.1ms
(CGO numbers reflect accel session overhead; actual Metal kernels
for MLDSASignBatch/VerifyBatch land in luxfi/accel and are routed
to CPU until lux-gpu publishes the FIPS 204-aware kernel.)
Mode-44 and mode-87 sign/verify now share the same accelerator
surface as mode-65 — adding new parameter sets in the future is one
row in the params table, not a kernel rewrite.
62 lines
1.2 KiB
Go
62 lines
1.2 KiB
Go
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package gpu
|
|
|
|
import "sync"
|
|
|
|
// capWorkers clamps the worker count to a sane minimum/maximum. We never
|
|
// want to fan out to fewer than 1 goroutine, and beyond ~64 the
|
|
// scheduler contention dominates the work.
|
|
//
|
|
// Lives in a build-tag-free helper file so both the cgo and !cgo
|
|
// engines can share it without duplication.
|
|
func capWorkers(n int) int {
|
|
if n < 1 {
|
|
return 1
|
|
}
|
|
if n > 64 {
|
|
return 64
|
|
}
|
|
return n
|
|
}
|
|
|
|
// parallelDo dispatches f over indices [0, total) across at most
|
|
// workers goroutines. Returns when every index has completed.
|
|
//
|
|
// Lives in a build-tag-free helper file so both the cgo and !cgo
|
|
// engines can share it.
|
|
func parallelDo(total, workers int, f func(int)) {
|
|
if total <= 0 {
|
|
return
|
|
}
|
|
if workers < 1 {
|
|
workers = 1
|
|
}
|
|
if workers > total {
|
|
workers = total
|
|
}
|
|
if workers == 1 {
|
|
for i := 0; i < total; i++ {
|
|
f(i)
|
|
}
|
|
return
|
|
}
|
|
var wg sync.WaitGroup
|
|
ch := make(chan int, total)
|
|
for i := 0; i < total; i++ {
|
|
ch <- i
|
|
}
|
|
close(ch)
|
|
wg.Add(workers)
|
|
for w := 0; w < workers; w++ {
|
|
go func() {
|
|
defer wg.Done()
|
|
for i := range ch {
|
|
f(i)
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
}
|