Files
magnetar/GPU-PORT-PLAN.md
T
Hanzo AI 6a3a8653dd audit 2026-06: TEE + GPU + public-permissionless-chain safety
Production-readiness audit of Magnetar v1.2 across four
dimensions: (1) public-permissionless-chain safety, (2) TEE
integration story, (3) GPU acceleration story, (4) canonical-name
positioning across luxfi/*. APPROVED for v1.2 deployment on Lux
public-permissionless chains under the documented threat model
(per-validator standalone unconditional; THBS-SE under
legacy-compat profile; strict-PQ chains route through sibling
luxfi/threshold/protocols/slhdsa-tee t-of-n attested-combiner
pool).

New documents:

  - AUDIT-2026-06.md --- comprehensive audit per dimension.
    Slashable evidence is correctly extractable
    (VerifyThbsSeEvidence is pure third-party). Slot-bound
    commit-and-reveal is sound (Class-N1 byte-equality pinned by
    TestSlhdsaInternal_ByteEqualToCirclSign). Static-corruption
    bound = Shamir t-of-n; adaptive bound = same with
    proactive-resharing lift proposed for v1.3. Public combiner
    is safe against malicious shares (commit re-derivation +
    ThbsSeShareEvidence). Residual microsecond-residency window
    of master bytes in derivedMaterial is honestly documented.
    Canonical-name verification: no conflicting "SLH-DSA
    threshold" name exists across luxfi/*; "Magnetar" is the
    single canonical name (precompile slot 0x012207, dispatcher
    pkg/thresholdd/magnetar.go, ZAP wire schemas/zap/magnetar_*,
    bridge consumer, crypto/slhdsa/gpu.go references).

  - TEE-INTEGRATION.md --- canonical design + deployment matrix.
    Profile gate (magnetarRefuseUnderStrictPQ) is ONE function in
    ONE place. AMD SEV-SNP is production today; TDX + NRAS are
    stubs tracked at luxfi/mpc#222 stages 2-3. NVIDIA
    Confidential Computing (H100/H200/B200) integration is a
    KnownIssuers map update once cc/attest/nras.go ships.
    Operator-side wiring reference: configureMagnetarStrictPQ
    composes ReleaseGate + hsm.Provider + ApprovalProvider into
    a CombinerPool with Threshold=2, RotationWindow=60s.

  - GPU-PORT-PLAN.md --- v1.3 work item MAGNETAR-GPU-PORT-V13.
    Four batched SHAKE256-based FIPS 205 hash-tree kernels at
    lux-private/gpu-kernels/ops/crypto/slhdsa/ (CUDA + HIP +
    Metal + Vulkan + WGSL) sit on top of the existing
    shake256/sha3_256/keccak256 Keccak-f[1600] permutation
    family. Estimated 16x sign throughput at batch=64 on Apple
    M1 Max Metal, 130x at batch=256 on NVIDIA H100. Threshold
    for GPU dispatch mirrors crypto/slhdsa.LastValidatorBatchTier
    pattern. NOT required for consensus-rate signing; for
    throughput consumers (bridge custody, N=100+ aggregate-cert
    verify, slashing-evidence sweep).

Updated:

  - BLOCKERS.md --- three proposed work items added:
    MAGNETAR-GPU-PORT-V13 (v1.3 GPU acceleration),
    MAGNETAR-PROACTIVE-RESHARE-V13 (v1.3 zero-secret refresh
    lifting static-corruption to refresh-window-bounded
    adaptive-corruption), MAGNETAR-APPLE-SE-HSM-V14 (v1.4
    hsm.Provider backed by Apple Keychain SE-only).

  - README.md --- Documents section links the three new specs.

Verification (clean as of audit):

  cd ref/go && GOWORK=off go build ./...          [OK]
  cd ref/go && GOWORK=off go vet ./...            [OK]
  cd ref/go && GOWORK=off go test -count=1 -short \
    -timeout 600s ./pkg/magnetar/...              [ok 192.033s]
  bash scripts/checks/strict-atom-ast.sh          [GATE GREEN]
2026-06-03 11:45:40 -07:00

14 KiB
Raw Blame History

Magnetar --- GPU acceleration port plan

This document scopes the v1.3 GPU-acceleration work item: landing four batched FIPS 205 SLH-DSA hash-tree kernels at lux-private/gpu-kernels/ops/crypto/slhdsa/ and wiring them through luxcpp/gpu into the magnetar slhSignAtom substrate.

Why this isn't already done. The shipped magnetar implementation (ref/go/pkg/magnetar/slhdsa_internal.go) walks FIPS 205 §5-§8 sequentially in pure Go. For consensus-rate signing (1 sig/block) this is sufficient on commodity CPU. The GPU path is for throughput consumers: bridge custody, N=100+ aggregate-cert verification, slashing-evidence sweep across historical blocks.


1. The substrate that already exists

lux-private/gpu-kernels/ops/crypto/ already ships:

Op Backends Purpose for Magnetar
shake256/ CUDA + HIP + Metal + Vulkan + WGSL FIPS 202 XOF; every SLH-DSA hash call goes through here
sha3_256/ CUDA + HIP + Metal + Vulkan + WGSL Same Keccak-f[1600] permutation, different domain separator
keccak256/ CUDA + HIP + Metal + Vulkan + WGSL Same permutation, Ethereum padding

Permission to share keccak_f1600.cuh is documented in the shake256/op.yaml notes block:

Shares the same Keccak-f[1600] permutation as keccak256 / sha3_256 / cshake256. The CUDA / HIP TUs pull in ../keccak256/keccak_f1600.cuh for one source of truth on the permutation; Vulkan + WGSL embed identical f1600 bodies in their respective shaders (GLSL/WGSL have no shared-include mechanism that survives the per-backend shader staging step).

The four magnetar kernels below sit on top of shake256 and add NO new permutation code --- they are FIPS 205-shape composition of existing SHAKE256 absorbs.


2. Kernel 1: magnetar_wotsplus_chain_batch

Signature

status = lux_gpu_magnetar_wotsplus_chain_batch(
    const lux_magnetar_wots_chain_input* in,   // batch of input tuples
    lux_magnetar_wots_chain_output* out,        // batch of output bytes
    size_t batch_size                            // M (>= 1)
);

struct lux_magnetar_wots_chain_input {
    const uint8_t* pk_seed;       // n bytes (n = 24 for M192s, 32 for M256s)
    uint8_t        addr[32];      // FIPS 205 §4.2 address (32 bytes for SHAKE)
    const uint8_t* x_base;        // n bytes (the chain base)
    uint32_t       i;             // starting hash address
    uint32_t       s;             // chain length (0..15 for w=16)
};

struct lux_magnetar_wots_chain_output {
    uint8_t out[32];   // n bytes (24 for M192s, 32 for M256s); upper bytes zero
};

Semantics

For each batch element (pk_seed, addr, x_base, i, s):

tmp ← x_base
for j ∈ [i, i+s):
    addr.setHashAddress(j)        // mutate bytes [28:32] of addr
    tmp ← SHAKE256(pk_seed || addr || tmp)[:n]
out ← tmp

This is FIPS 205 §5 Algorithm 5 chain, executed independently for each batch element.

Parallelism

  • Across batch elements: trivial, one CUDA thread / Metal thread / Vulkan workgroup per element.
  • Within one element: NONE (chain is sequential by construction).

Use sites in ref/go/pkg/magnetar/slhdsa_internal.go

  • wotsChain line 402 — invoked inside wotsSign:442 and wotsPkFromSig:645.
  • wotsSign line 442 — wotsLen = 2n + 3 chain computations per WOTS+ signature.
  • wotsPkGen line 499 — same.
  • wotsPkFromSig line 645 — used in xmssPkFromSig:607, which is invoked d-1 times during hypertree sign (verify-side recomputation for the chained-root case).

Volume per signature (M192s shape: n=24, d=7, hPrime=9, wotsLen=51)

  • One hypertree sign: d × 2^hPrime × wotsLen chain computations for full PK gen at one layer (only at layer 0, the leaves): 1 × 512 × 51 = 26,112 chains.
  • WOTS+ sign per leaf: wotsLen = 51 chains.
  • Total per signature: ~26,000-30,000 chains. At GPU throughput of ~10⁷ chains/sec on H100 → ~3ms per signature for the WOTS+ kernel alone.

3. Kernel 2: magnetar_fors_subtree_batch

Signature

status = lux_gpu_magnetar_fors_subtree_batch(
    const lux_magnetar_fors_subtree_input* in,
    lux_magnetar_fors_subtree_output* out,
    size_t batch_size
);

struct lux_magnetar_fors_subtree_input {
    const uint8_t* pk_seed;        // n bytes
    uint8_t        addr[32];       // FIPS 205 §4.2 address
    uint32_t       leaf_idx;       // FORS subtree leaf index
    uint32_t       height;         // FORS subtree height to compute
    // The secret-derived FORS seed is provided host-side via
    // the prfOut callback; per-element callers pre-populate the
    // sk_leaves array with the PRF outputs for the relevant
    // leaves.
    const uint8_t* sk_leaves;      // (2^height) × n bytes
};

struct lux_magnetar_fors_subtree_output {
    uint8_t out[32];   // n bytes (the subtree node value)
};

Semantics

For each batch element, compute the FORS subtree node at (leaf_idx, height) using F(pk_seed, addr, sk_leaf_i) at the leaves and H(pk_seed, addr, left || right) at internal nodes. FIPS 205 §8.2 Algorithm 15.

Parallelism

  • Across batch elements: one thread block per element.
  • Within one element: across the 2^height leaves and the pairwise reductions at each height level (binary-tree reduction).

Use sites

  • forsNodeCompute line 735.
  • forsSign line 780 — k subtrees per signature; for M192s, k=17 subtrees of height a=14.

Volume per signature (M192s shape)

  • k × (2^a - 1) = 17 × 16383 = ~280K SHAKE absorbs.
  • Plus the secret-derived sk_leaf computations: k × 2^a = ~278K PRF callbacks (handled on the host inside the strict-atom callback).

4. Kernel 3: magnetar_xmss_subtree_batch

Signature

status = lux_gpu_magnetar_xmss_subtree_batch(
    const lux_magnetar_xmss_subtree_input* in,
    lux_magnetar_xmss_subtree_output* out,
    size_t batch_size
);

struct lux_magnetar_xmss_subtree_input {
    const uint8_t* pk_seed;        // n bytes
    uint8_t        addr[32];
    uint32_t       leaf_idx;
    uint32_t       height;
    // Host pre-populates the WOTS+ public-key values at the
    // leaves of this subtree.
    const uint8_t* wots_pk_leaves; // (2^height) × n bytes
};

struct lux_magnetar_xmss_subtree_output {
    uint8_t out[32];   // n bytes (the subtree node value)
};

Semantics

For each batch element, compute the XMSS subtree node at (leaf_idx, height) using H(pk_seed, addr, left || right) internal nodes. FIPS 205 §6.1 Algorithm 9.

The WOTS+ leaf computation is delegated to kernel 1; this kernel takes the resulting leaf bytes as input and performs the pairwise hash reductions.

Parallelism

  • Across batch elements: one thread block per element.
  • Within one element: binary-tree reduction.

Use sites

  • xmssNodeCompute line 546.
  • xmssSign line 582 — auth-path computation (hPrime sibling nodes per XMSS layer).
  • xmssPkFromSig line 607 — verifier-side recomputation.

Volume per signature (M192s shape: hPrime=9, d=7)

  • Per XMSS sign: hPrime auth-path nodes; each requires a subtree node at some height. Total: ~256 SHAKE absorbs per XMSS sign.
  • Per hypertree sign: d × xmss_sign_cost = 7 × ~256 = ~1,800 SHAKE absorbs.

5. Kernel 4: magnetar_hmsg_prfmsg_batch

Signature

status = lux_gpu_magnetar_hmsg_prfmsg_batch(
    const lux_magnetar_hmsg_input*   hmsg_in,    // batch of H_msg inputs
    lux_magnetar_hmsg_output*        hmsg_out,
    const lux_magnetar_prfmsg_input* prfmsg_in,  // batch of PRF_msg inputs
    lux_magnetar_prfmsg_output*      prfmsg_out,
    size_t hmsg_batch,
    size_t prfmsg_batch
);

struct lux_magnetar_hmsg_input {
    const uint8_t* r;        // n bytes
    const uint8_t* pk_seed;  // n bytes
    const uint8_t* pk_root;  // n bytes
    const uint8_t* msg_prime;
    size_t         msg_prime_len;
};

struct lux_magnetar_hmsg_output {
    uint8_t digest[64];   // m bytes (39 for M192s, 42 for M192f, 47 for M256s)
};

struct lux_magnetar_prfmsg_input {
    const uint8_t* sk_prf_segment;  // n bytes; provided by host via strict-atom callback
    const uint8_t* opt_rand;         // n bytes
    const uint8_t* msg_prime;
    size_t         msg_prime_len;
};

struct lux_magnetar_prfmsg_output {
    uint8_t out[32];    // n bytes
};

Semantics

H_msg(R, PK.seed, PK.root, M') and PRF_msg(SK.prf, optRand, M') per FIPS 205 §11.2 SHAKE instantiation. Both are single SHAKE256 absorb-and-squeeze calls.

Parallelism

Trivial across batch elements.

Use sites

  • hMsgPub line 341 — exactly one per signature.
  • prfMsg callback — exactly one per signature.

Volume per signature

2 SHAKE absorbs (one per kernel). These exist as a separate kernel only because batching them across many independent signatures amortizes the kernel launch overhead.


6. File layout

lux-private/gpu-kernels/
└── ops/
    └── crypto/
        └── slhdsa/                      # NEW
            ├── op.yaml                  # op metadata; 5 backends
            ├── cuda.cu                  # 4 kernels in one TU
            ├── hip.cu                   # mirror, __HIPCC__-gated
            ├── metal.metal              # MSL source
            ├── vulkan/                  # GLSL sources
            │   ├── wotsplus_chain.comp
            │   ├── fors_subtree.comp
            │   ├── xmss_subtree.comp
            │   └── hmsg_prfmsg.comp
            └── wgsl/                    # WGSL sources
                ├── wotsplus_chain.wgsl
                ├── fors_subtree.wgsl
                ├── xmss_subtree.wgsl
                └── hmsg_prfmsg.wgsl

Plus the C ABI extension at luxcpp/gpu/include/lux/gpu.h:

// SLH-DSA hash-tree batched primitives. See ops/crypto/slhdsa/op.yaml.
int lux_gpu_magnetar_wotsplus_chain_batch(
    const lux_magnetar_wots_chain_input*  in,
    lux_magnetar_wots_chain_output*       out,
    size_t                                 batch_size);

int lux_gpu_magnetar_fors_subtree_batch(/* ... */);
int lux_gpu_magnetar_xmss_subtree_batch(/* ... */);
int lux_gpu_magnetar_hmsg_prfmsg_batch(/* ... */);

Plus the backend plugin ABI extension at luxcpp/gpu/include/lux/gpu/backend_plugin.h (v9 bump):

typedef int (*op_magnetar_wotsplus_chain_batch_fn)(/* same shape */);
// Three more.

CPU backend reference implementation at luxcpp/gpu/src/cpu_backend.cpp::cpu_op_magnetar_wotsplus_chain_batch mirrors wotsChain in slhdsa_internal.go --- per-element sequential, no parallelism inside the chain --- so CPU oracle is the reference for byte-identity tests.


7. Go-side wiring

The pluggability seam in magnetar is the existing prfOut callback (slhdsa_internal.go:286). For the GPU acceleration path we add ONE additional seam: a publicHashBatch interface that the four hash-tree primitives dispatch to when available.

// PublicHashBatch is the optional batched-SHAKE substrate.
// Implementations include luxcpp/gpu via cgo bindings.
// The strict-atom Combine path holds a PublicHashBatch reference
// and dispatches to it for the four kernels above when the
// batch size exceeds the dispatch threshold.
type PublicHashBatch interface {
    WotsPlusChainBatch(in []WotsChainInput, out []WotsChainOutput) error
    ForsSubtreeBatch(in []ForsSubtreeInput, out []ForsSubtreeOutput) error
    XmssSubtreeBatch(in []XmssSubtreeInput, out []XmssSubtreeOutput) error
    HMsgPrfMsgBatch(hmsgIn []HMsgInput, hmsgOut []HMsgOutput,
                    prfMsgIn []PrfMsgInput, prfMsgOut []PrfMsgOutput) error
}

Wiring at assembleSignatureBytes and ValidatorBatchVerify is the same shape luxfi/crypto/slhdsa/gpu.go already uses for the existing luxfi/accel-based path: detect the substrate via accel.Available(), route batches above the threshold to GPU, fall back to CPU for small batches.


8. Expected throughput

Conservative estimates (numbers grounded in the existing luxfi/crypto/slhdsa GPU benches and the published shake256 kernel performance on Apple M1 Max + NVIDIA H100):

Op CPU 1-core (M1 Max) GPU M1 Max Metal (batch 64) GPU H100 (batch 256)
SLH-DSA-SHAKE-192s sign ~80 ms ~5 ms (16x) ~0.6 ms (130x)
SLH-DSA-SHAKE-192s verify ~2 ms ~0.15 ms (13x) ~0.05 ms (40x)

Threshold for GPU dispatch

Matches the existing crypto/slhdsa.LastValidatorBatchTier pattern:

Tier Batch size Backend
Serial 1..63 CPU single-core
Concurrent 64..511 CPU goroutine-parallel
GPU 512+ luxcpp/gpu (if Available)

The break-even at 512 reflects the kernel-launch overhead on discrete GPUs; on Apple Silicon UMA the break-even is lower (~64 elements).


9. Verification + tests

Each kernel is byte-identity tested against the CPU oracle (which is the existing slhdsa_internal.go walk):

ops/crypto/slhdsa/tests/
├── test_magnetar_wotsplus_chain_kat.cpp     # NIST FIPS 205 KAT
├── test_magnetar_fors_subtree_kat.cpp
├── test_magnetar_xmss_subtree_kat.cpp
└── test_magnetar_hmsg_prfmsg_kat.cpp

Each test:

  1. Runs the CPU oracle on a fixed input batch.
  2. Runs the active GPU plugin on the same batch.
  3. Byte-compares outputs.

The vector source is FIPS 205 Algorithm 22 (slh_sign) intermediate states, captured from cloudflare/circl/sign/slhdsa at the four seams. Vectors live at lux-private/gpu-kernels/ops/crypto/slhdsa/vectors/.


10. v1.3 work-item summary

Task Estimate Owner
Land 4 CUDA kernels + tests 5 days tbd
Land 4 HIP kernels (port from CUDA via macro gating) 1 day tbd
Land 4 Metal kernels + KAT 4 days tbd
Land 4 Vulkan GLSL kernels + KAT 4 days tbd
Land 4 WGSL kernels + KAT 3 days tbd
luxcpp/gpu C ABI + plugin ABI extension (v8 → v9) 2 days tbd
CPU oracle in luxcpp/gpu/src/cpu_backend.cpp 2 days tbd
Magnetar Go-side PublicHashBatch interface + wiring 3 days tbd
luxfi/crypto/slhdsa/gpu.go dispatch update 1 day tbd
Bench suite + leaderboard cells 2 days tbd
Total ~27 days

Status: PROPOSED. Not v1.2-blocking.


Document metadata

  • Name: GPU-PORT-PLAN.md
  • Date: 2026-06-03
  • Tracks: BLOCKERS.md::MAGNETAR-GPU-PORT-V13