diff --git a/bindings/cabi/main.go b/bindings/cabi/main.go new file mode 100644 index 0000000..83712fc --- /dev/null +++ b/bindings/cabi/main.go @@ -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() {} diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml new file mode 100644 index 0000000..bc31cd4 --- /dev/null +++ b/bindings/rust/Cargo.toml @@ -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 = [] diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs new file mode 100644 index 0000000..6c1ec56 --- /dev/null +++ b/bindings/rust/build.rs @@ -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()); + } + } + } +} diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs new file mode 100644 index 0000000..c0cccda --- /dev/null +++ b/bindings/rust/src/lib.rs @@ -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(¶ms).unwrap(); +//! let sk = keygen.gen_secret_key().unwrap(); +//! let bsk = keygen.gen_bootstrap_key(&sk).unwrap(); +//! +//! let enc = Fhe::encryptor(¶ms, &sk).unwrap(); +//! let dec = Fhe::decryptor(¶ms, &sk).unwrap(); +//! let eval = Fhe::evaluator(¶ms, &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 = std::result::Result; + +// ── 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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> { + 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> { + 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> { + 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> { + 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 { + 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 { + 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 { + if cts.len() != 8 { + return Err(FheError::FFIError("decrypt_byte: expected 8 ciphertexts", -1)); + } + let handles: Vec = 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 { + if cts.len() != 32 { + return Err(FheError::FFIError("decrypt_uint32: expected 32 ciphertexts", -1)); + } + let handles: Vec = 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 { + if cts.len() != 64 { + return Err(FheError::FFIError("decrypt_uint64: expected 64 ciphertexts", -1)); + } + let handles: Vec = 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 = 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 { + 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 { + 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 { + 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 { + 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 { + 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(¶ms).unwrap(); + let sk = keygen.gen_secret_key().unwrap(); + + let enc = Fhe::encryptor(¶ms, &sk).unwrap(); + let dec = Fhe::decryptor(¶ms, &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(¶ms).unwrap(); + let sk = keygen.gen_secret_key().unwrap(); + + let enc = Fhe::encryptor(¶ms, &sk).unwrap(); + let dec = Fhe::decryptor(¶ms, &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(¶ms).unwrap(); + let sk = keygen.gen_secret_key().unwrap(); + let bsk = keygen.gen_bootstrap_key(&sk).unwrap(); + + let enc = Fhe::encryptor(¶ms, &sk).unwrap(); + let dec = Fhe::decryptor(¶ms, &sk).unwrap(); + let eval = Fhe::evaluator(¶ms, &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(¶ms).unwrap(); + let (sk, _pk) = keygen.gen_keypair().unwrap(); + + let enc = Fhe::encryptor(¶ms, &sk).unwrap(); + let dec = Fhe::decryptor(¶ms, &sk).unwrap(); + + let ct = enc.encrypt(true).unwrap(); + assert!(dec.decrypt(&ct).unwrap()); + } +}