feat: C FFI + Rust bindings (libluxfhe)

This commit is contained in:
Hanzo AI
2026-04-04 12:45:53 -07:00
parent 03be1b4356
commit 94a4332b52
4 changed files with 1088 additions and 0 deletions
+514
View File
@@ -0,0 +1,514 @@
// Package main exports lux/fhe functions as a C shared library.
//
// Build:
//
// CGO_ENABLED=1 go build -buildmode=c-shared -o dist/libluxfhe.so ./bindings/cabi/
// CGO_ENABLED=1 go build -buildmode=c-shared -o dist/libluxfhe.dylib ./bindings/cabi/ # macOS
// install_name_tool -id @rpath/libluxfhe.dylib dist/libluxfhe.dylib # macOS rpath
//
// This produces libluxfhe.{so,dylib,dll} + libluxfhe.h
// which Python (ctypes/cffi), TypeScript (N-API/WASM), and Rust (FFI) can bind to.
//
// All stateful Go objects (parameters, keys, encryptors, etc.) are stored behind
// opaque uint64 handles. A global handle map prevents GC collection.
package main
import "C"
import (
"sync"
"sync/atomic"
"unsafe"
"github.com/luxfi/fhe"
)
// ═══════════════════════════════════════════════════════════════════════
// Handle map — opaque uint64 IDs prevent Go GC from collecting objects
// ═══════════════════════════════════════════════════════════════════════
var (
handleMu sync.RWMutex
handleMap = make(map[C.ulonglong]any)
nextHandle atomic.Uint64
)
func storeHandle(obj any) C.ulonglong {
h := C.ulonglong(nextHandle.Add(1))
handleMu.Lock()
handleMap[h] = obj
handleMu.Unlock()
return h
}
func loadHandle[T any](h C.ulonglong) (T, bool) {
handleMu.RLock()
obj, ok := handleMap[h]
handleMu.RUnlock()
if !ok {
var zero T
return zero, false
}
val, ok := obj.(T)
return val, ok
}
func dropHandle(h C.ulonglong) {
handleMu.Lock()
delete(handleMap, h)
handleMu.Unlock()
}
// ═══════════════════════════════════════════════════════════════════════
// Handle free
// ═══════════════════════════════════════════════════════════════════════
//export lux_fhe_drop
func lux_fhe_drop(handle C.ulonglong) {
dropHandle(handle)
}
// ═══════════════════════════════════════════════════════════════════════
// Parameters
// ═══════════════════════════════════════════════════════════════════════
//export lux_fhe_params_pn10qp27
func lux_fhe_params_pn10qp27(out *C.ulonglong) C.int {
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
if err != nil {
return -1
}
*out = storeHandle(params)
return 0
}
// ═══════════════════════════════════════════════════════════════════════
// KeyGenerator
// ═══════════════════════════════════════════════════════════════════════
//export lux_fhe_keygen_new
func lux_fhe_keygen_new(paramsH C.ulonglong, out *C.ulonglong) C.int {
params, ok := loadHandle[fhe.Parameters](paramsH)
if !ok {
return -1
}
kg := fhe.NewKeyGenerator(params)
*out = storeHandle(kg)
return 0
}
//export lux_fhe_keygen_gen_secret_key
func lux_fhe_keygen_gen_secret_key(kgH C.ulonglong, out *C.ulonglong) C.int {
kg, ok := loadHandle[*fhe.KeyGenerator](kgH)
if !ok {
return -1
}
sk := kg.GenSecretKey()
*out = storeHandle(sk)
return 0
}
//export lux_fhe_keygen_gen_public_key
func lux_fhe_keygen_gen_public_key(kgH C.ulonglong, skH C.ulonglong, out *C.ulonglong) C.int {
kg, ok := loadHandle[*fhe.KeyGenerator](kgH)
if !ok {
return -1
}
sk, ok := loadHandle[*fhe.SecretKey](skH)
if !ok {
return -2
}
pk := kg.GenPublicKey(sk)
*out = storeHandle(pk)
return 0
}
//export lux_fhe_keygen_gen_keypair
func lux_fhe_keygen_gen_keypair(kgH C.ulonglong, skOut *C.ulonglong, pkOut *C.ulonglong) C.int {
kg, ok := loadHandle[*fhe.KeyGenerator](kgH)
if !ok {
return -1
}
sk, pk := kg.GenKeyPair()
*skOut = storeHandle(sk)
*pkOut = storeHandle(pk)
return 0
}
//export lux_fhe_keygen_gen_bootstrap_key
func lux_fhe_keygen_gen_bootstrap_key(kgH C.ulonglong, skH C.ulonglong, out *C.ulonglong) C.int {
kg, ok := loadHandle[*fhe.KeyGenerator](kgH)
if !ok {
return -1
}
sk, ok := loadHandle[*fhe.SecretKey](skH)
if !ok {
return -2
}
bsk := kg.GenBootstrapKey(sk)
*out = storeHandle(bsk)
return 0
}
// ═══════════════════════════════════════════════════════════════════════
// Encryptor
// ═══════════════════════════════════════════════════════════════════════
//export lux_fhe_encryptor_new
func lux_fhe_encryptor_new(paramsH C.ulonglong, skH C.ulonglong, out *C.ulonglong) C.int {
params, ok := loadHandle[fhe.Parameters](paramsH)
if !ok {
return -1
}
sk, ok := loadHandle[*fhe.SecretKey](skH)
if !ok {
return -2
}
enc := fhe.NewEncryptor(params, sk)
*out = storeHandle(enc)
return 0
}
//export lux_fhe_encrypt
func lux_fhe_encrypt(encH C.ulonglong, value C.int, out *C.ulonglong) C.int {
enc, ok := loadHandle[*fhe.Encryptor](encH)
if !ok {
return -1
}
ct := enc.Encrypt(value != 0)
*out = storeHandle(ct)
return 0
}
//export lux_fhe_encrypt_bit
func lux_fhe_encrypt_bit(encH C.ulonglong, bit C.int, out *C.ulonglong) C.int {
enc, ok := loadHandle[*fhe.Encryptor](encH)
if !ok {
return -1
}
ct := enc.EncryptBit(int(bit))
*out = storeHandle(ct)
return 0
}
//export lux_fhe_encrypt_byte
func lux_fhe_encrypt_byte(encH C.ulonglong, b C.uchar, out *C.ulonglong) C.int {
enc, ok := loadHandle[*fhe.Encryptor](encH)
if !ok {
return -1
}
cts := enc.EncryptByte(byte(b))
for i := 0; i < 8; i++ {
out_i := (*C.ulonglong)(pointerOffset(out, i))
*out_i = storeHandle(cts[i])
}
return 0
}
//export lux_fhe_encrypt_uint32
func lux_fhe_encrypt_uint32(encH C.ulonglong, v C.uint, out *C.ulonglong) C.int {
enc, ok := loadHandle[*fhe.Encryptor](encH)
if !ok {
return -1
}
cts := enc.EncryptUint32(uint32(v))
for i := 0; i < 32; i++ {
out_i := (*C.ulonglong)(pointerOffset(out, i))
*out_i = storeHandle(cts[i])
}
return 0
}
//export lux_fhe_encrypt_uint64
func lux_fhe_encrypt_uint64(encH C.ulonglong, v C.ulonglong, out *C.ulonglong) C.int {
enc, ok := loadHandle[*fhe.Encryptor](encH)
if !ok {
return -1
}
cts := enc.EncryptUint64(uint64(v))
for i := 0; i < 64; i++ {
out_i := (*C.ulonglong)(pointerOffset(out, i))
*out_i = storeHandle(cts[i])
}
return 0
}
//export lux_fhe_encrypt_uint256
func lux_fhe_encrypt_uint256(encH C.ulonglong, limbs *C.ulonglong, out *C.ulonglong) C.int {
enc, ok := loadHandle[*fhe.Encryptor](encH)
if !ok {
return -1
}
var v [4]uint64
for i := 0; i < 4; i++ {
v[i] = uint64(*(*C.ulonglong)(pointerOffset(limbs, i)))
}
cts := enc.EncryptUint256(v)
for i := 0; i < 256; i++ {
out_i := (*C.ulonglong)(pointerOffset(out, i))
*out_i = storeHandle(cts[i])
}
return 0
}
// ═══════════════════════════════════════════════════════════════════════
// Decryptor
// ═══════════════════════════════════════════════════════════════════════
//export lux_fhe_decryptor_new
func lux_fhe_decryptor_new(paramsH C.ulonglong, skH C.ulonglong, out *C.ulonglong) C.int {
params, ok := loadHandle[fhe.Parameters](paramsH)
if !ok {
return -1
}
sk, ok := loadHandle[*fhe.SecretKey](skH)
if !ok {
return -2
}
dec := fhe.NewDecryptor(params, sk)
*out = storeHandle(dec)
return 0
}
//export lux_fhe_decrypt
func lux_fhe_decrypt(decH C.ulonglong, ctH C.ulonglong, out *C.int) C.int {
dec, ok := loadHandle[*fhe.Decryptor](decH)
if !ok {
return -1
}
ct, ok := loadHandle[*fhe.Ciphertext](ctH)
if !ok {
return -2
}
if dec.Decrypt(ct) {
*out = 1
} else {
*out = 0
}
return 0
}
//export lux_fhe_decrypt_bit
func lux_fhe_decrypt_bit(decH C.ulonglong, ctH C.ulonglong, out *C.int) C.int {
dec, ok := loadHandle[*fhe.Decryptor](decH)
if !ok {
return -1
}
ct, ok := loadHandle[*fhe.Ciphertext](ctH)
if !ok {
return -2
}
*out = C.int(dec.DecryptBit(ct))
return 0
}
//export lux_fhe_decrypt_byte
func lux_fhe_decrypt_byte(decH C.ulonglong, handles *C.ulonglong, out *C.uchar) C.int {
dec, ok := loadHandle[*fhe.Decryptor](decH)
if !ok {
return -1
}
var cts [8]*fhe.Ciphertext
for i := 0; i < 8; i++ {
h := *(*C.ulonglong)(pointerOffset(handles, i))
ct, ok := loadHandle[*fhe.Ciphertext](h)
if !ok {
return -2
}
cts[i] = ct
}
*out = C.uchar(dec.DecryptByte(cts))
return 0
}
//export lux_fhe_decrypt_uint32
func lux_fhe_decrypt_uint32(decH C.ulonglong, handles *C.ulonglong, out *C.uint) C.int {
dec, ok := loadHandle[*fhe.Decryptor](decH)
if !ok {
return -1
}
var cts [32]*fhe.Ciphertext
for i := 0; i < 32; i++ {
h := *(*C.ulonglong)(pointerOffset(handles, i))
ct, ok := loadHandle[*fhe.Ciphertext](h)
if !ok {
return -2
}
cts[i] = ct
}
*out = C.uint(dec.DecryptUint32(cts))
return 0
}
//export lux_fhe_decrypt_uint64
func lux_fhe_decrypt_uint64(decH C.ulonglong, handles *C.ulonglong, out *C.ulonglong) C.int {
dec, ok := loadHandle[*fhe.Decryptor](decH)
if !ok {
return -1
}
var cts [64]*fhe.Ciphertext
for i := 0; i < 64; i++ {
h := *(*C.ulonglong)(pointerOffset(handles, i))
ct, ok := loadHandle[*fhe.Ciphertext](h)
if !ok {
return -2
}
cts[i] = ct
}
*out = C.ulonglong(dec.DecryptUint64(cts))
return 0
}
//export lux_fhe_decrypt_uint256
func lux_fhe_decrypt_uint256(decH C.ulonglong, handles *C.ulonglong, out *C.ulonglong) C.int {
dec, ok := loadHandle[*fhe.Decryptor](decH)
if !ok {
return -1
}
var cts [256]*fhe.Ciphertext
for i := 0; i < 256; i++ {
h := *(*C.ulonglong)(pointerOffset(handles, i))
ct, ok := loadHandle[*fhe.Ciphertext](h)
if !ok {
return -2
}
cts[i] = ct
}
v := dec.DecryptUint256(cts)
for i := 0; i < 4; i++ {
out_i := (*C.ulonglong)(pointerOffset(out, i))
*out_i = C.ulonglong(v[i])
}
return 0
}
// ═══════════════════════════════════════════════════════════════════════
// Evaluator (boolean gates)
// ═══════════════════════════════════════════════════════════════════════
//export lux_fhe_evaluator_new
func lux_fhe_evaluator_new(paramsH C.ulonglong, bskH C.ulonglong, out *C.ulonglong) C.int {
params, ok := loadHandle[fhe.Parameters](paramsH)
if !ok {
return -1
}
bsk, ok := loadHandle[*fhe.BootstrapKey](bskH)
if !ok {
return -2
}
eval := fhe.NewEvaluator(params, bsk)
*out = storeHandle(eval)
return 0
}
//export lux_fhe_eval_and
func lux_fhe_eval_and(evalH C.ulonglong, ct1H C.ulonglong, ct2H C.ulonglong, out *C.ulonglong) C.int {
eval, ok := loadHandle[*fhe.Evaluator](evalH)
if !ok {
return -1
}
ct1, ok := loadHandle[*fhe.Ciphertext](ct1H)
if !ok {
return -2
}
ct2, ok := loadHandle[*fhe.Ciphertext](ct2H)
if !ok {
return -3
}
result, err := eval.AND(ct1, ct2)
if err != nil {
return -10
}
*out = storeHandle(result)
return 0
}
//export lux_fhe_eval_or
func lux_fhe_eval_or(evalH C.ulonglong, ct1H C.ulonglong, ct2H C.ulonglong, out *C.ulonglong) C.int {
eval, ok := loadHandle[*fhe.Evaluator](evalH)
if !ok {
return -1
}
ct1, ok := loadHandle[*fhe.Ciphertext](ct1H)
if !ok {
return -2
}
ct2, ok := loadHandle[*fhe.Ciphertext](ct2H)
if !ok {
return -3
}
result, err := eval.OR(ct1, ct2)
if err != nil {
return -10
}
*out = storeHandle(result)
return 0
}
//export lux_fhe_eval_xor
func lux_fhe_eval_xor(evalH C.ulonglong, ct1H C.ulonglong, ct2H C.ulonglong, out *C.ulonglong) C.int {
eval, ok := loadHandle[*fhe.Evaluator](evalH)
if !ok {
return -1
}
ct1, ok := loadHandle[*fhe.Ciphertext](ct1H)
if !ok {
return -2
}
ct2, ok := loadHandle[*fhe.Ciphertext](ct2H)
if !ok {
return -3
}
result, err := eval.XOR(ct1, ct2)
if err != nil {
return -10
}
*out = storeHandle(result)
return 0
}
//export lux_fhe_eval_not
func lux_fhe_eval_not(evalH C.ulonglong, ctH C.ulonglong, out *C.ulonglong) C.int {
eval, ok := loadHandle[*fhe.Evaluator](evalH)
if !ok {
return -1
}
ct, ok := loadHandle[*fhe.Ciphertext](ctH)
if !ok {
return -2
}
result := eval.NOT(ct)
*out = storeHandle(result)
return 0
}
//export lux_fhe_eval_nand
func lux_fhe_eval_nand(evalH C.ulonglong, ct1H C.ulonglong, ct2H C.ulonglong, out *C.ulonglong) C.int {
eval, ok := loadHandle[*fhe.Evaluator](evalH)
if !ok {
return -1
}
ct1, ok := loadHandle[*fhe.Ciphertext](ct1H)
if !ok {
return -2
}
ct2, ok := loadHandle[*fhe.Ciphertext](ct2H)
if !ok {
return -3
}
result, err := eval.NAND(ct1, ct2)
if err != nil {
return -10
}
*out = storeHandle(result)
return 0
}
// ═══════════════════════════════════════════════════════════════════════
// Pointer arithmetic helper for array access across CGo boundary
// ═══════════════════════════════════════════════════════════════════════
func pointerOffset(base *C.ulonglong, index int) unsafe.Pointer {
return unsafe.Add(unsafe.Pointer(base), index*8) // 8 bytes per uint64
}
func main() {}
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "luxfhe-sys"
version = "0.1.0"
edition = "2021"
description = "FFI bindings to libluxfhe — FHE boolean gates via Lux lattice cryptography"
license = "BSD-3-Clause"
repository = "https://github.com/luxfi/fhe"
keywords = ["fhe", "homomorphic", "encryption", "lattice", "boolean-gates"]
categories = ["cryptography", "external-ffi-bindings"]
[dependencies]
thiserror = "2"
[build-dependencies]
[features]
default = []
+36
View File
@@ -0,0 +1,36 @@
use std::env;
use std::path::PathBuf;
fn main() {
// Search order for libluxfhe:
// 1. LUX_FHE_LIB_DIR env var
// 2. ~/work/lux/fhe/dist/
// 3. System library path
if let Ok(lib_dir) = env::var("LUX_FHE_LIB_DIR") {
println!("cargo:rustc-link-search=native={lib_dir}");
} else {
// Dev default: ~/work/lux/fhe/dist/
if let Some(home) = env::var_os("HOME") {
let dist = PathBuf::from(home).join("work/lux/fhe/dist");
if dist.exists() {
println!("cargo:rustc-link-search=native={}", dist.display());
}
}
}
println!("cargo:rustc-link-lib=dylib=luxfhe");
// macOS: set rpath for all link targets (binaries, tests, examples)
if cfg!(target_os = "macos") {
// Always include /usr/local/lib as rpath fallback
println!("cargo:rustc-link-arg=-Wl,-rpath,/usr/local/lib");
if let Some(home) = env::var_os("HOME") {
let dist = PathBuf::from(home).join("work/lux/fhe/dist");
if dist.exists() {
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", dist.display());
}
}
}
}
+521
View File
@@ -0,0 +1,521 @@
//! FFI bindings to libluxfhe — FHE boolean gates on encrypted data.
//!
//! One library, one implementation: libluxfhe (luxfi/lattice via Go).
//! Same FHE from Lux blockchain to AI agents.
//!
//! All Go objects are behind opaque `u64` handles. The Rust wrappers
//! provide safe, RAII-managed access — handles are freed on drop.
//!
//! # Example
//!
//! ```rust,ignore
//! use luxfhe_sys::Fhe;
//!
//! let params = Fhe::params_pn10qp27().unwrap();
//! let keygen = Fhe::keygen(&params).unwrap();
//! let sk = keygen.gen_secret_key().unwrap();
//! let bsk = keygen.gen_bootstrap_key(&sk).unwrap();
//!
//! let enc = Fhe::encryptor(&params, &sk).unwrap();
//! let dec = Fhe::decryptor(&params, &sk).unwrap();
//! let eval = Fhe::evaluator(&params, &bsk).unwrap();
//!
//! let ct_t = enc.encrypt(true).unwrap();
//! let ct_f = enc.encrypt(false).unwrap();
//!
//! let ct_and = eval.and(&ct_t, &ct_f).unwrap();
//! assert!(!dec.decrypt(&ct_and).unwrap());
//! ```
use std::os::raw::c_int;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum FheError {
#[error("libluxfhe operation failed: {0} (rc={1})")]
FFIError(&'static str, i32),
}
pub type Result<T> = std::result::Result<T, FheError>;
// ── Raw FFI ──────────────────────────────────────────────────────────
extern "C" {
fn lux_fhe_drop(handle: u64);
// Parameters
fn lux_fhe_params_pn10qp27(out: *mut u64) -> c_int;
// KeyGenerator
fn lux_fhe_keygen_new(params: u64, out: *mut u64) -> c_int;
fn lux_fhe_keygen_gen_secret_key(kg: u64, out: *mut u64) -> c_int;
fn lux_fhe_keygen_gen_public_key(kg: u64, sk: u64, out: *mut u64) -> c_int;
fn lux_fhe_keygen_gen_keypair(kg: u64, sk_out: *mut u64, pk_out: *mut u64) -> c_int;
fn lux_fhe_keygen_gen_bootstrap_key(kg: u64, sk: u64, out: *mut u64) -> c_int;
// Encryptor
fn lux_fhe_encryptor_new(params: u64, sk: u64, out: *mut u64) -> c_int;
fn lux_fhe_encrypt(enc: u64, value: c_int, out: *mut u64) -> c_int;
fn lux_fhe_encrypt_bit(enc: u64, bit: c_int, out: *mut u64) -> c_int;
fn lux_fhe_encrypt_byte(enc: u64, b: u8, out: *mut u64) -> c_int;
fn lux_fhe_encrypt_uint32(enc: u64, v: u32, out: *mut u64) -> c_int;
fn lux_fhe_encrypt_uint64(enc: u64, v: u64, out: *mut u64) -> c_int;
fn lux_fhe_encrypt_uint256(enc: u64, limbs: *const u64, out: *mut u64) -> c_int;
// Decryptor
fn lux_fhe_decryptor_new(params: u64, sk: u64, out: *mut u64) -> c_int;
fn lux_fhe_decrypt(dec: u64, ct: u64, out: *mut c_int) -> c_int;
fn lux_fhe_decrypt_bit(dec: u64, ct: u64, out: *mut c_int) -> c_int;
fn lux_fhe_decrypt_byte(dec: u64, handles: *const u64, out: *mut u8) -> c_int;
fn lux_fhe_decrypt_uint32(dec: u64, handles: *const u64, out: *mut u32) -> c_int;
fn lux_fhe_decrypt_uint64(dec: u64, handles: *const u64, out: *mut u64) -> c_int;
fn lux_fhe_decrypt_uint256(dec: u64, handles: *const u64, out: *mut u64) -> c_int;
// Evaluator (gates)
fn lux_fhe_evaluator_new(params: u64, bsk: u64, out: *mut u64) -> c_int;
fn lux_fhe_eval_and(eval: u64, ct1: u64, ct2: u64, out: *mut u64) -> c_int;
fn lux_fhe_eval_or(eval: u64, ct1: u64, ct2: u64, out: *mut u64) -> c_int;
fn lux_fhe_eval_xor(eval: u64, ct1: u64, ct2: u64, out: *mut u64) -> c_int;
fn lux_fhe_eval_not(eval: u64, ct: u64, out: *mut u64) -> c_int;
fn lux_fhe_eval_nand(eval: u64, ct1: u64, ct2: u64, out: *mut u64) -> c_int;
}
// ── Handle wrapper (RAII) ────────────────────────────────────────────
/// Opaque handle to a Go-side FHE object. Freed on drop.
#[derive(Debug)]
struct Handle(u64);
impl Handle {
fn id(&self) -> u64 {
self.0
}
}
impl Drop for Handle {
fn drop(&mut self) {
if self.0 != 0 {
unsafe { lux_fhe_drop(self.0) };
}
}
}
// ── Public types ─────────────────────────────────────────────────────
/// FHE parameter set.
#[derive(Debug)]
pub struct Params(Handle);
/// FHE key generator.
#[derive(Debug)]
pub struct KeyGen(Handle);
/// FHE secret key.
#[derive(Debug)]
pub struct SecretKey(Handle);
/// FHE public key.
#[derive(Debug)]
pub struct PublicKey(Handle);
/// FHE bootstrap key (evaluation key).
#[derive(Debug)]
pub struct BootstrapKey(Handle);
/// FHE encryptor (requires secret key).
#[derive(Debug)]
pub struct Encryptor(Handle);
/// FHE decryptor (requires secret key).
#[derive(Debug)]
pub struct Decryptor(Handle);
/// FHE evaluator (requires bootstrap key, no secret key).
#[derive(Debug)]
pub struct Evaluator(Handle);
/// Encrypted bit (ciphertext).
#[derive(Debug)]
pub struct Ciphertext(Handle);
// ── Namespace ────────────────────────────────────────────────────────
/// Top-level FHE operations.
pub struct Fhe;
impl Fhe {
/// Create PN10QP27 parameters (~128-bit security).
pub fn params_pn10qp27() -> Result<Params> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_params_pn10qp27(&mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_params_pn10qp27", rc));
}
Ok(Params(Handle(h)))
}
/// Create a key generator for the given parameters.
pub fn keygen(params: &Params) -> Result<KeyGen> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_keygen_new(params.0.id(), &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_keygen_new", rc));
}
Ok(KeyGen(Handle(h)))
}
/// Create an encryptor (requires secret key).
pub fn encryptor(params: &Params, sk: &SecretKey) -> Result<Encryptor> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_encryptor_new(params.0.id(), sk.0.id(), &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_encryptor_new", rc));
}
Ok(Encryptor(Handle(h)))
}
/// Create a decryptor (requires secret key).
pub fn decryptor(params: &Params, sk: &SecretKey) -> Result<Decryptor> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_decryptor_new(params.0.id(), sk.0.id(), &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_decryptor_new", rc));
}
Ok(Decryptor(Handle(h)))
}
/// Create an evaluator (requires bootstrap key, no secret key needed).
pub fn evaluator(params: &Params, bsk: &BootstrapKey) -> Result<Evaluator> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_evaluator_new(params.0.id(), bsk.0.id(), &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_evaluator_new", rc));
}
Ok(Evaluator(Handle(h)))
}
}
// ── KeyGen ───────────────────────────────────────────────────────────
impl KeyGen {
/// Generate a secret key.
pub fn gen_secret_key(&self) -> Result<SecretKey> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_keygen_gen_secret_key(self.0.id(), &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_keygen_gen_secret_key", rc));
}
Ok(SecretKey(Handle(h)))
}
/// Generate a public key from a secret key.
pub fn gen_public_key(&self, sk: &SecretKey) -> Result<PublicKey> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_keygen_gen_public_key(self.0.id(), sk.0.id(), &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_keygen_gen_public_key", rc));
}
Ok(PublicKey(Handle(h)))
}
/// Generate a key pair (secret key + public key).
pub fn gen_keypair(&self) -> Result<(SecretKey, PublicKey)> {
let mut sk_h: u64 = 0;
let mut pk_h: u64 = 0;
let rc = unsafe { lux_fhe_keygen_gen_keypair(self.0.id(), &mut sk_h, &mut pk_h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_keygen_gen_keypair", rc));
}
Ok((SecretKey(Handle(sk_h)), PublicKey(Handle(pk_h))))
}
/// Generate a bootstrap key from a secret key.
pub fn gen_bootstrap_key(&self, sk: &SecretKey) -> Result<BootstrapKey> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_keygen_gen_bootstrap_key(self.0.id(), sk.0.id(), &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_keygen_gen_bootstrap_key", rc));
}
Ok(BootstrapKey(Handle(h)))
}
}
// ── Encryptor ────────────────────────────────────────────────────────
impl Encryptor {
/// Encrypt a boolean value.
pub fn encrypt(&self, value: bool) -> Result<Ciphertext> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_encrypt(self.0.id(), value as c_int, &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_encrypt", rc));
}
Ok(Ciphertext(Handle(h)))
}
/// Encrypt a bit (0 or 1).
pub fn encrypt_bit(&self, bit: i32) -> Result<Ciphertext> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_encrypt_bit(self.0.id(), bit as c_int, &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_encrypt_bit", rc));
}
Ok(Ciphertext(Handle(h)))
}
/// Encrypt a byte as 8 ciphertexts (LSB first).
pub fn encrypt_byte(&self, b: u8) -> Result<Vec<Ciphertext>> {
let mut handles = [0u64; 8];
let rc = unsafe { lux_fhe_encrypt_byte(self.0.id(), b, handles.as_mut_ptr()) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_encrypt_byte", rc));
}
Ok(handles.into_iter().map(|h| Ciphertext(Handle(h))).collect())
}
/// Encrypt a u32 as 32 ciphertexts (LSB first).
pub fn encrypt_uint32(&self, v: u32) -> Result<Vec<Ciphertext>> {
let mut handles = [0u64; 32];
let rc = unsafe { lux_fhe_encrypt_uint32(self.0.id(), v, handles.as_mut_ptr()) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_encrypt_uint32", rc));
}
Ok(handles.into_iter().map(|h| Ciphertext(Handle(h))).collect())
}
/// Encrypt a u64 as 64 ciphertexts (LSB first).
pub fn encrypt_uint64(&self, v: u64) -> Result<Vec<Ciphertext>> {
let mut handles = [0u64; 64];
let rc = unsafe { lux_fhe_encrypt_uint64(self.0.id(), v, handles.as_mut_ptr()) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_encrypt_uint64", rc));
}
Ok(handles.into_iter().map(|h| Ciphertext(Handle(h))).collect())
}
/// Encrypt a 256-bit value (4 u64 limbs, little-endian) as 256 ciphertexts.
pub fn encrypt_uint256(&self, limbs: &[u64; 4]) -> Result<Vec<Ciphertext>> {
let mut handles = [0u64; 256];
let rc = unsafe {
lux_fhe_encrypt_uint256(self.0.id(), limbs.as_ptr(), handles.as_mut_ptr())
};
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_encrypt_uint256", rc));
}
Ok(handles.into_iter().map(|h| Ciphertext(Handle(h))).collect())
}
}
// ── Decryptor ────────────────────────────────────────────────────────
impl Decryptor {
/// Decrypt a ciphertext to a boolean.
pub fn decrypt(&self, ct: &Ciphertext) -> Result<bool> {
let mut out: c_int = 0;
let rc = unsafe { lux_fhe_decrypt(self.0.id(), ct.0.id(), &mut out) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_decrypt", rc));
}
Ok(out != 0)
}
/// Decrypt a ciphertext to a bit (0 or 1).
pub fn decrypt_bit(&self, ct: &Ciphertext) -> Result<i32> {
let mut out: c_int = 0;
let rc = unsafe { lux_fhe_decrypt_bit(self.0.id(), ct.0.id(), &mut out) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_decrypt_bit", rc));
}
Ok(out)
}
/// Decrypt 8 ciphertexts to a byte.
pub fn decrypt_byte(&self, cts: &[Ciphertext]) -> Result<u8> {
if cts.len() != 8 {
return Err(FheError::FFIError("decrypt_byte: expected 8 ciphertexts", -1));
}
let handles: Vec<u64> = cts.iter().map(|ct| ct.0.id()).collect();
let mut out: u8 = 0;
let rc = unsafe { lux_fhe_decrypt_byte(self.0.id(), handles.as_ptr(), &mut out) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_decrypt_byte", rc));
}
Ok(out)
}
/// Decrypt 32 ciphertexts to a u32.
pub fn decrypt_uint32(&self, cts: &[Ciphertext]) -> Result<u32> {
if cts.len() != 32 {
return Err(FheError::FFIError("decrypt_uint32: expected 32 ciphertexts", -1));
}
let handles: Vec<u64> = cts.iter().map(|ct| ct.0.id()).collect();
let mut out: u32 = 0;
let rc = unsafe { lux_fhe_decrypt_uint32(self.0.id(), handles.as_ptr(), &mut out) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_decrypt_uint32", rc));
}
Ok(out)
}
/// Decrypt 64 ciphertexts to a u64.
pub fn decrypt_uint64(&self, cts: &[Ciphertext]) -> Result<u64> {
if cts.len() != 64 {
return Err(FheError::FFIError("decrypt_uint64: expected 64 ciphertexts", -1));
}
let handles: Vec<u64> = cts.iter().map(|ct| ct.0.id()).collect();
let mut out: u64 = 0;
let rc = unsafe { lux_fhe_decrypt_uint64(self.0.id(), handles.as_ptr(), &mut out) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_decrypt_uint64", rc));
}
Ok(out)
}
/// Decrypt 256 ciphertexts to 4 u64 limbs (little-endian).
pub fn decrypt_uint256(&self, cts: &[Ciphertext]) -> Result<[u64; 4]> {
if cts.len() != 256 {
return Err(FheError::FFIError("decrypt_uint256: expected 256 ciphertexts", -1));
}
let handles: Vec<u64> = cts.iter().map(|ct| ct.0.id()).collect();
let mut out = [0u64; 4];
let rc = unsafe { lux_fhe_decrypt_uint256(self.0.id(), handles.as_ptr(), out.as_mut_ptr()) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_decrypt_uint256", rc));
}
Ok(out)
}
}
// ── Evaluator ────────────────────────────────────────────────────────
impl Evaluator {
/// Homomorphic AND gate.
pub fn and(&self, ct1: &Ciphertext, ct2: &Ciphertext) -> Result<Ciphertext> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_eval_and(self.0.id(), ct1.0.id(), ct2.0.id(), &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_eval_and", rc));
}
Ok(Ciphertext(Handle(h)))
}
/// Homomorphic OR gate.
pub fn or(&self, ct1: &Ciphertext, ct2: &Ciphertext) -> Result<Ciphertext> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_eval_or(self.0.id(), ct1.0.id(), ct2.0.id(), &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_eval_or", rc));
}
Ok(Ciphertext(Handle(h)))
}
/// Homomorphic XOR gate.
pub fn xor(&self, ct1: &Ciphertext, ct2: &Ciphertext) -> Result<Ciphertext> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_eval_xor(self.0.id(), ct1.0.id(), ct2.0.id(), &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_eval_xor", rc));
}
Ok(Ciphertext(Handle(h)))
}
/// Homomorphic NOT gate (free — no bootstrapping).
pub fn not(&self, ct: &Ciphertext) -> Result<Ciphertext> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_eval_not(self.0.id(), ct.0.id(), &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_eval_not", rc));
}
Ok(Ciphertext(Handle(h)))
}
/// Homomorphic NAND gate.
pub fn nand(&self, ct1: &Ciphertext, ct2: &Ciphertext) -> Result<Ciphertext> {
let mut h: u64 = 0;
let rc = unsafe { lux_fhe_eval_nand(self.0.id(), ct1.0.id(), ct2.0.id(), &mut h) };
if rc != 0 {
return Err(FheError::FFIError("lux_fhe_eval_nand", rc));
}
Ok(Ciphertext(Handle(h)))
}
}
// ── Ciphertext handle access ─────────────────────────────────────────
impl Ciphertext {
/// Get the raw handle ID (for advanced FFI usage).
pub fn handle_id(&self) -> u64 {
self.0.id()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encrypt_decrypt_bool() {
let params = Fhe::params_pn10qp27().unwrap();
let keygen = Fhe::keygen(&params).unwrap();
let sk = keygen.gen_secret_key().unwrap();
let enc = Fhe::encryptor(&params, &sk).unwrap();
let dec = Fhe::decryptor(&params, &sk).unwrap();
let ct_t = enc.encrypt(true).unwrap();
let ct_f = enc.encrypt(false).unwrap();
assert!(dec.decrypt(&ct_t).unwrap());
assert!(!dec.decrypt(&ct_f).unwrap());
}
#[test]
fn test_encrypt_decrypt_byte() {
let params = Fhe::params_pn10qp27().unwrap();
let keygen = Fhe::keygen(&params).unwrap();
let sk = keygen.gen_secret_key().unwrap();
let enc = Fhe::encryptor(&params, &sk).unwrap();
let dec = Fhe::decryptor(&params, &sk).unwrap();
let cts = enc.encrypt_byte(0xA5).unwrap();
let result = dec.decrypt_byte(&cts).unwrap();
assert_eq!(result, 0xA5);
}
#[test]
fn test_and_gate() {
let params = Fhe::params_pn10qp27().unwrap();
let keygen = Fhe::keygen(&params).unwrap();
let sk = keygen.gen_secret_key().unwrap();
let bsk = keygen.gen_bootstrap_key(&sk).unwrap();
let enc = Fhe::encryptor(&params, &sk).unwrap();
let dec = Fhe::decryptor(&params, &sk).unwrap();
let eval = Fhe::evaluator(&params, &bsk).unwrap();
let ct_t = enc.encrypt(true).unwrap();
let ct_f = enc.encrypt(false).unwrap();
// AND(true, true) = true
let r = eval.and(&ct_t, &enc.encrypt(true).unwrap()).unwrap();
assert!(dec.decrypt(&r).unwrap());
// AND(true, false) = false
let r = eval.and(&ct_t, &ct_f).unwrap();
assert!(!dec.decrypt(&r).unwrap());
}
#[test]
fn test_keypair() {
let params = Fhe::params_pn10qp27().unwrap();
let keygen = Fhe::keygen(&params).unwrap();
let (sk, _pk) = keygen.gen_keypair().unwrap();
let enc = Fhe::encryptor(&params, &sk).unwrap();
let dec = Fhe::decryptor(&params, &sk).unwrap();
let ct = enc.encrypt(true).unwrap();
assert!(dec.decrypt(&ct).unwrap());
}
}