mirror of
https://github.com/luxfi/fhe.git
synced 2026-07-26 23:16:08 +00:00
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).
92 lines
2.3 KiB
Go
92 lines
2.3 KiB
Go
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
//go:build tfhe_encrypt_ct
|
|
|
|
// encrypt_ct.go -- cgo bridge exposing TFHE Encrypt to the C dudect
|
|
// harness in dudect_encrypt.c.
|
|
//
|
|
// CT POPULATION (operational framing):
|
|
// Both dudect classes are VALID Encrypt invocations differing in
|
|
// the secret bit being encrypted:
|
|
// class A: always encrypt(false)
|
|
// class B: encrypt(random bit)
|
|
// Any timing difference between classes is a real bit-dependent
|
|
// timing in the Encrypt pipeline.
|
|
//
|
|
// The fixture uses Lux's default parameter set PN10QP27 with a
|
|
// freshly-generated secret key. Each dudect sample re-invokes
|
|
// Encrypt; the leakage profile is the per-call wall-clock + cycle
|
|
// counter as collected by dudect's measurement loop.
|
|
//
|
|
// Build (Linux):
|
|
// GOWORK=off go build -buildmode=c-shared \
|
|
// -o libtfhe_encrypt.so ./encrypt_ct.go
|
|
// Build (macOS):
|
|
// GOWORK=off go build -buildmode=c-shared \
|
|
// -o libtfhe_encrypt.dylib ./encrypt_ct.go
|
|
|
|
package main
|
|
|
|
/*
|
|
#cgo arm64 CFLAGS: -include ${SRCDIR}/dudect_compat.h
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"unsafe"
|
|
|
|
"github.com/luxfi/fhe"
|
|
)
|
|
|
|
// Long-lived fixture.
|
|
var (
|
|
fhParams fhe.Parameters
|
|
fhSecret *fhe.SecretKey
|
|
fhEncrypt *fhe.Encryptor
|
|
)
|
|
|
|
//export tfhe_encrypt_ct_setup
|
|
//
|
|
// Initialise the long-lived fixture. Returns 0 on success.
|
|
func tfhe_encrypt_ct_setup() C.int {
|
|
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
|
|
if err != nil {
|
|
return 1
|
|
}
|
|
kg := fhe.NewKeyGenerator(params)
|
|
sk := kg.GenSecretKey()
|
|
fhParams = params
|
|
fhSecret = sk
|
|
fhEncrypt = fhe.NewEncryptor(params, sk)
|
|
return 0
|
|
}
|
|
|
|
//export tfhe_encrypt_ct_input_size
|
|
//
|
|
// Returns the per-sample input width: 1 byte (the bit to encrypt,
|
|
// where any non-zero value is `true`).
|
|
func tfhe_encrypt_ct_input_size() C.size_t {
|
|
return C.size_t(1)
|
|
}
|
|
|
|
//export tfhe_encrypt_ct
|
|
//
|
|
// One dudect measurement sample. `data` points to a 1-byte input.
|
|
// data[0] = 0 => encrypt(false); data[0] != 0 => encrypt(true).
|
|
//
|
|
// This call must NOT branch on the input bit beyond the conversion
|
|
// from byte to bool.
|
|
func tfhe_encrypt_ct(data *C.uint8_t) {
|
|
if fhEncrypt == nil {
|
|
return
|
|
}
|
|
src := unsafe.Slice((*byte)(unsafe.Pointer(data)), 1)
|
|
bit := src[0] != 0
|
|
_, _ = fhEncrypt.EncryptSafe(bit)
|
|
}
|
|
|
|
func main() {}
|