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