Files
crypto/slhdsa/provenance_test.go
Hanzo AI 589cd47a92 crypto: runtime dispatch provenance for mldsa+slhdsa, ML-DSA-44/87 batch tests
Adds Provenance() on slhdsa/mldsa packages so reviewers can ask the
running binary "which dispatch tier is live right now?" instead of
trusting a static claim. Honest by construction: a release build
without the SLH-DSA / ML-DSA backend plugin reports TierAccelCPUFallback
or TierGoroutineParallelCPU, never TierGPUSubstrate.

The strong-symbol observation is recorded by batchVerifyGPU /
batchSignGPU after a successful C ABI dispatch; subsequent
GetProvenance() calls report TierGPUSubstrate. The cache only goes
0 -> 1 so transient tensor allocation failures cannot regress
provenance to "no plugin".

mldsa/batch.go: 3-tier dispatch (GPU -> goroutine-parallel CPU ->
serial CPU), mirroring slhdsa. concurrentBatchThreshold=8 tuned for
the FIPS 204 verify cost.

ML-DSA-44 and ML-DSA-87 batch tests pin KAT replay + CPU/GPU
equivalence + tamper rejection across the dispatch tiers. Previously
only ML-DSA-65 had this coverage.

Removed dead pq/slhdsa/gpu/gpu.go (replaced by the live dispatcher
in slhdsa/gpu.go).
2026-05-21 14:11:16 -07:00

52 lines
2.1 KiB
Go

// Copyright (C) 2020-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package slhdsa
import (
"testing"
)
// TestProvenance_NonEmpty is a smoke test that pins the API: a release
// binary MUST be able to introspect its own dispatch tier without an
// error path or panic. This is the function a release reviewer or CI
// gate calls to confirm that the "GPU accelerated SLH-DSA" claim is
// not vapor.
func TestProvenance_NonEmpty(t *testing.T) {
p := GetProvenance()
if p.Tier == TierUnknown {
t.Fatal("GetProvenance returned TierUnknown — dispatcher cannot determine its own state")
}
if p.SignBatchThresholdN <= 0 {
t.Errorf("SignBatchThresholdN = %d (expected positive)", p.SignBatchThresholdN)
}
if p.ConcurrentSignThresholdN <= 0 {
t.Errorf("ConcurrentSignThresholdN = %d (expected positive)", p.ConcurrentSignThresholdN)
}
t.Logf("SLH-DSA dispatch provenance: tier=%s accel=%v device=%v plugin_strong=%v sign_threshold=%d concurrent_threshold=%d",
p.Tier, p.AccelInitialised, p.DeviceAvailable, p.PluginStrongSymbol,
p.SignBatchThresholdN, p.ConcurrentSignThresholdN)
}
// TestProvenance_TierFlowsToCPUWhenNoPlugin pins the contract: when
// the SLH-DSA backend plugin is not loaded (which is the case for the
// in-tree default build), GetProvenance MUST report a CPU tier and NOT
// claim GPU. This is the test that protects against a future
// regression where someone defaults TierGPUSubstrate as the "happy
// path" without verifying plugin load.
func TestProvenance_TierFlowsToCPUWhenNoPlugin(t *testing.T) {
p := GetProvenance()
if p.Tier == TierGPUSubstrate && !p.PluginStrongSymbol {
t.Fatalf("Provenance reported TierGPUSubstrate without PluginStrongSymbol — false GPU claim")
}
// The default build does NOT ship a plugin. Whichever CPU tier we
// land on (accel-cpu-fallback when accel init succeeded, goroutine-
// parallel-cpu when it did not), the binary is honest about it.
switch p.Tier {
case TierGPUSubstrate, TierAccelCPUFallback, TierGoroutineParallelCPU, TierSerialCPU:
// OK — any one of these is a truthful tier.
default:
t.Fatalf("unexpected tier %v (%d)", p.Tier, p.Tier)
}
}