Files
fhe/ct/dudect/bootstrap_ct.go
T
Hanzo AI a3e4beaecc fhe/ct/dudect: harness for Encrypt + Decrypt + Bootstrap CT
Empirical constant-time analysis harness mirroring the Pulsar
pack at ~/work/lux/pulsar/ct/dudect/:

  encrypt_ct.go   + dudect_encrypt.c    - cgo bridge + C main loop
  decrypt_ct.go   + dudect_decrypt.c    - the CT-critical routine
  bootstrap_ct.go + dudect_bootstrap.c  - PBS composite

CT population (operational framing):
  - Both classes are VALID inputs to the routine under test
  - Class A is a fixed input; class B is uniformly drawn from a
    pre-built pool of K independent valid inputs
  - Any timing difference is a real secret-content signal

All three cgo shared libraries build clean:
  GOWORK=off go build -buildmode=c-shared -tags tfhe_encrypt_ct
  GOWORK=off go build -buildmode=c-shared -tags tfhe_decrypt_ct
  GOWORK=off go build -buildmode=c-shared -tags tfhe_bootstrap_ct

dudect.h fetched on demand via fetch.sh (not committed).
dudect_compat.h supplies _mm_mfence/__rdtsc on AArch64 hosts.

Submission-grade run pending (GATE-1 in CRYPTOGRAPHER-SIGN-OFF.md).
2026-05-19 11:30:28 -07:00

111 lines
2.8 KiB
Go

// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//go:build tfhe_bootstrap_ct
// bootstrap_ct.go -- cgo bridge exposing TFHE PBS to the C dudect
// harness in dudect_bootstrap.c.
//
// CT POPULATION:
// Both dudect classes are VALID Bootstrap invocations on
// pre-generated valid LWE ciphertexts encoding different secret
// bits:
// class A: always ciphertext_pool[0] (fixed)
// class B: ciphertext_pool[rand % pool_size] (varying valid)
//
// The bootstrap operation is the heaviest TFHE primitive (~50-80 ms
// per call on M2 Pro for the NTT-fused fast path). Each dudect
// sample re-runs the full PBS; the harness defaults are smaller
// than for verify/decrypt (5000 samples/batch x 100 batches).
//
// Build:
// GOWORK=off go build -buildmode=c-shared \
// -o libtfhe_bootstrap.{so,dylib} ./bootstrap_ct.go
package main
/*
#cgo arm64 CFLAGS: -include ${SRCDIR}/dudect_compat.h
#include <stdint.h>
#include <stddef.h>
*/
import "C"
import (
"crypto/rand"
"unsafe"
"github.com/luxfi/fhe"
)
const kBootstrapValidPool = 16
var (
bFhParams fhe.Parameters
bFhSecret *fhe.SecretKey
bFhEval *fhe.Evaluator
bFhBSK *fhe.BootstrapKey
bFhPool [kBootstrapValidPool]*fhe.Ciphertext
)
//export tfhe_bootstrap_ct_setup
//
// Initialise the long-lived fixture. Returns 0 on success.
func tfhe_bootstrap_ct_setup() C.int {
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
if err != nil {
return 1
}
kg := fhe.NewKeyGenerator(params)
sk := kg.GenSecretKey()
bsk := kg.GenBootstrapKey(sk)
enc := fhe.NewEncryptor(params, sk)
for i := 0; i < kBootstrapValidPool; i++ {
var b [1]byte
if _, err := rand.Read(b[:]); err != nil {
return 4
}
ct, err := enc.EncryptSafe(b[0]&1 == 1)
if err != nil {
return 5
}
bFhPool[i] = ct
}
bFhParams = params
bFhSecret = sk
bFhBSK = bsk
bFhEval = fhe.NewEvaluator(params, bsk)
return 0
}
//export tfhe_bootstrap_ct_pool_size
func tfhe_bootstrap_ct_pool_size() C.size_t {
return C.size_t(kBootstrapValidPool)
}
//export tfhe_bootstrap_ct_input_size
func tfhe_bootstrap_ct_input_size() C.size_t {
return C.size_t(4)
}
//export tfhe_bootstrap_ct
//
// One dudect measurement sample. data points to a 4-byte big-endian
// uint32 pool index. We read the index, mod it against the pool
// size, and PBS the indexed ciphertext through the NAND LUT (most
// CT-uniform choice).
func tfhe_bootstrap_ct(data *C.uint8_t) {
if bFhEval == nil {
return
}
src := unsafe.Slice((*byte)(unsafe.Pointer(data)), 4)
idx := (uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])) %
uint32(kBootstrapValidPool)
// NAND exercises the PBS pipeline with a deterministic LUT.
_, _ = bFhEval.NAND(bFhPool[idx], bFhPool[idx])
}
func main() {}