mirror of
https://github.com/luxfi/fhe.git
synced 2026-07-26 23:16:08 +00:00
feat: C FFI + Rust bindings (libluxfhe)
This commit is contained in:
@@ -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() {}
|
||||||
@@ -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 = []
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<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(¶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());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user