Files
fhe/gpu/cgo/tfhe_bridge.cpp
T
Zach Kelling 3675b37845 fix: update GPU build tags and reorganize CGO files
- Move tfhe_bridge.cpp/h to cgo/ subdirectory
- Change fhe_ops.go to use luxgpu tag (opt-in for GPU)
- Change fhe_test.go to use luxgpu tag
- Ensure pure Go fallback works for both CGO=0 and CGO=1
2026-01-04 13:42:26 -08:00

1152 lines
37 KiB
C++

// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2025, Lux Industries Inc
//
// C++ bridge implementation for OpenFHE BinFHE (TFHE) operations
// This bridges Go's CGO calls to OpenFHE's C++ API
#include "tfhe_bridge.h"
#include <binfhecontext.h>
#include <utils/serial.h>
#include <vector>
#include <sstream>
#include <cstring>
#include <memory>
using namespace lbcrypto;
// Version
#define TFHE_BRIDGE_VERSION 0x00010000 // 1.0.0
// =============================================================================
// Internal Context Wrapper
// =============================================================================
struct TfheContextInternal {
BinFHEContext context;
BINFHE_PARAMSET paramset;
BINFHE_METHOD method;
LWEPrivateKey secretKey;
bool hasSecretKey;
bool hasBootstrapKey;
TfheContextInternal() : hasSecretKey(false), hasBootstrapKey(false) {}
};
// Internal integer wrapper (bit-vector representation)
struct TfheIntegerInternal {
std::vector<LWECiphertext> bits;
TfheIntType itype;
TfheIntegerInternal(TfheIntType t) : itype(t) {
bits.resize(static_cast<int>(t));
}
TfheIntegerInternal(int bitLen) {
if (bitLen <= 4) itype = TFHE_UINT4;
else if (bitLen <= 8) itype = TFHE_UINT8;
else if (bitLen <= 16) itype = TFHE_UINT16;
else if (bitLen <= 32) itype = TFHE_UINT32;
else if (bitLen <= 64) itype = TFHE_UINT64;
else if (bitLen <= 128) itype = TFHE_UINT128;
else if (bitLen <= 160) itype = TFHE_UINT160;
else itype = TFHE_UINT256;
bits.resize(static_cast<int>(itype));
}
int numBits() const { return static_cast<int>(itype); }
};
// =============================================================================
// Helper Functions
// =============================================================================
static BINFHE_PARAMSET mapSecurityLevel(TfheSecurityLevel level) {
switch (level) {
case TFHE_TOY: return TOY;
case TFHE_STD128: return STD128;
case TFHE_STD128_AP: return STD128_AP;
case TFHE_STD128_LMKCDEY: return STD128_LMKCDEY;
case TFHE_STD192: return STD192;
case TFHE_STD256: return STD256;
default: return STD128;
}
}
static BINFHE_METHOD mapMethod(TfheMethod method) {
switch (method) {
case TFHE_METHOD_GINX: return GINX;
case TFHE_METHOD_AP: return AP;
case TFHE_METHOD_LMKCDEY: return LMKCDEY;
default: return GINX;
}
}
// =============================================================================
// Context Management
// =============================================================================
extern "C" TfheContext tfhe_context_new(TfheSecurityLevel level, TfheMethod method) {
try {
auto* ctx = new TfheContextInternal();
ctx->paramset = mapSecurityLevel(level);
ctx->method = mapMethod(method);
ctx->context = BinFHEContext();
ctx->context.GenerateBinFHEContext(ctx->paramset, ctx->method);
return static_cast<TfheContext>(ctx);
} catch (...) {
return nullptr;
}
}
extern "C" void tfhe_context_free(TfheContext ctx) {
if (ctx) {
delete static_cast<TfheContextInternal*>(ctx);
}
}
extern "C" uint32_t tfhe_version(void) {
return TFHE_BRIDGE_VERSION;
}
// =============================================================================
// Key Generation
// =============================================================================
extern "C" TfheSecretKey tfhe_keygen(TfheContext ctx) {
if (!ctx) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto sk = internal->context.KeyGen();
internal->secretKey = sk;
internal->hasSecretKey = true;
auto* skCopy = new LWEPrivateKey(sk);
return static_cast<TfheSecretKey>(skCopy);
} catch (...) {
return nullptr;
}
}
extern "C" void tfhe_secretkey_free(TfheSecretKey sk) {
if (sk) {
delete static_cast<LWEPrivateKey*>(sk);
}
}
extern "C" void tfhe_secret_key_free(TfheSecretKey sk) {
tfhe_secretkey_free(sk);
}
extern "C" int tfhe_bootstrap_keygen(TfheContext ctx, TfheSecretKey sk) {
if (!ctx || !sk) return -1;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* skPtr = static_cast<LWEPrivateKey*>(sk);
internal->context.BTKeyGen(*skPtr);
internal->hasBootstrapKey = true;
return 0;
} catch (...) {
return -1;
}
}
extern "C" int tfhe_keyswitch_keygen(TfheContext ctx, TfheSecretKey sk) {
if (!ctx || !sk) return -1;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* skPtr = static_cast<LWEPrivateKey*>(sk);
internal->context.BTKeyGen(*skPtr); // BTKeyGen includes key switching
return 0;
} catch (...) {
return -1;
}
}
extern "C" bool tfhe_has_bootstrap_key(TfheContext ctx) {
if (!ctx) return false;
auto* internal = static_cast<TfheContextInternal*>(ctx);
return internal->hasBootstrapKey;
}
// =============================================================================
// Public Key Generation
// =============================================================================
extern "C" TfhePublicKey tfhe_public_keygen(TfheContext ctx, TfheSecretKey sk) {
// OpenFHE BinFHE doesn't have separate public key - return nullptr
// Public key encryption uses a different mechanism
(void)ctx;
(void)sk;
return nullptr;
}
extern "C" void tfhe_public_key_free(TfhePublicKey pk) {
(void)pk;
// No-op for now
}
extern "C" int tfhe_publickey_serialize(TfhePublicKey pk, uint8_t** out, size_t* out_len) {
(void)pk;
(void)out;
(void)out_len;
return -1; // Not implemented
}
extern "C" TfhePublicKey tfhe_publickey_deserialize(TfheContext ctx, const uint8_t* data, size_t len) {
(void)ctx;
(void)data;
(void)len;
return nullptr; // Not implemented
}
// =============================================================================
// Boolean Encryption / Decryption
// =============================================================================
extern "C" TfheCiphertext tfhe_encrypt(TfheContext ctx, TfheSecretKey sk, int value) {
if (!ctx || !sk) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* skPtr = static_cast<LWEPrivateKey*>(sk);
auto ct = internal->context.Encrypt(*skPtr, value != 0 ? 1 : 0);
auto* ctCopy = new LWECiphertext(ct);
return static_cast<TfheCiphertext>(ctCopy);
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_encrypt_bit(TfheContext ctx, TfheSecretKey sk, int value) {
return tfhe_encrypt(ctx, sk, value);
}
extern "C" TfheCiphertext tfhe_encrypt_bit_public(TfheContext ctx, TfhePublicKey pk, int value) {
// BinFHE doesn't have public key encryption in the traditional sense
(void)ctx;
(void)pk;
(void)value;
return nullptr;
}
extern "C" int tfhe_decrypt(TfheContext ctx, TfheSecretKey sk, TfheCiphertext ct) {
if (!ctx || !sk || !ct) return -1;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* skPtr = static_cast<LWEPrivateKey*>(sk);
auto* ctPtr = static_cast<LWECiphertext*>(ct);
LWEPlaintext result;
internal->context.Decrypt(*skPtr, *ctPtr, &result);
return result;
} catch (...) {
return -1;
}
}
extern "C" int tfhe_decrypt_bit(TfheContext ctx, TfheSecretKey sk, TfheCiphertext ct) {
return tfhe_decrypt(ctx, sk, ct);
}
extern "C" void tfhe_ciphertext_free(TfheCiphertext ct) {
if (ct) {
delete static_cast<LWECiphertext*>(ct);
}
}
extern "C" TfheCiphertext tfhe_ciphertext_clone(TfheCiphertext ct) {
if (!ct) return nullptr;
try {
auto* ctPtr = static_cast<LWECiphertext*>(ct);
return static_cast<TfheCiphertext>(new LWECiphertext(*ctPtr));
} catch (...) {
return nullptr;
}
}
// =============================================================================
// Boolean Gates
// =============================================================================
extern "C" TfheCiphertext tfhe_and(TfheContext ctx, TfheCiphertext ct1, TfheCiphertext ct2) {
if (!ctx || !ct1 || !ct2) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* ct1Ptr = static_cast<LWECiphertext*>(ct1);
auto* ct2Ptr = static_cast<LWECiphertext*>(ct2);
auto result = internal->context.EvalBinGate(AND, *ct1Ptr, *ct2Ptr);
return static_cast<TfheCiphertext>(new LWECiphertext(result));
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_or(TfheContext ctx, TfheCiphertext ct1, TfheCiphertext ct2) {
if (!ctx || !ct1 || !ct2) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* ct1Ptr = static_cast<LWECiphertext*>(ct1);
auto* ct2Ptr = static_cast<LWECiphertext*>(ct2);
auto result = internal->context.EvalBinGate(OR, *ct1Ptr, *ct2Ptr);
return static_cast<TfheCiphertext>(new LWECiphertext(result));
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_xor(TfheContext ctx, TfheCiphertext ct1, TfheCiphertext ct2) {
if (!ctx || !ct1 || !ct2) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* ct1Ptr = static_cast<LWECiphertext*>(ct1);
auto* ct2Ptr = static_cast<LWECiphertext*>(ct2);
auto result = internal->context.EvalBinGate(XOR, *ct1Ptr, *ct2Ptr);
return static_cast<TfheCiphertext>(new LWECiphertext(result));
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_nand(TfheContext ctx, TfheCiphertext ct1, TfheCiphertext ct2) {
if (!ctx || !ct1 || !ct2) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* ct1Ptr = static_cast<LWECiphertext*>(ct1);
auto* ct2Ptr = static_cast<LWECiphertext*>(ct2);
auto result = internal->context.EvalBinGate(NAND, *ct1Ptr, *ct2Ptr);
return static_cast<TfheCiphertext>(new LWECiphertext(result));
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_nor(TfheContext ctx, TfheCiphertext ct1, TfheCiphertext ct2) {
if (!ctx || !ct1 || !ct2) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* ct1Ptr = static_cast<LWECiphertext*>(ct1);
auto* ct2Ptr = static_cast<LWECiphertext*>(ct2);
auto result = internal->context.EvalBinGate(NOR, *ct1Ptr, *ct2Ptr);
return static_cast<TfheCiphertext>(new LWECiphertext(result));
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_xnor(TfheContext ctx, TfheCiphertext ct1, TfheCiphertext ct2) {
if (!ctx || !ct1 || !ct2) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* ct1Ptr = static_cast<LWECiphertext*>(ct1);
auto* ct2Ptr = static_cast<LWECiphertext*>(ct2);
auto result = internal->context.EvalBinGate(XNOR, *ct1Ptr, *ct2Ptr);
return static_cast<TfheCiphertext>(new LWECiphertext(result));
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_not(TfheContext ctx, TfheCiphertext ct) {
if (!ctx || !ct) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* ctPtr = static_cast<LWECiphertext*>(ct);
auto result = internal->context.EvalNOT(*ctPtr);
return static_cast<TfheCiphertext>(new LWECiphertext(result));
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_mux(TfheContext ctx, TfheCiphertext sel, TfheCiphertext ct1, TfheCiphertext ct2) {
if (!ctx || !sel || !ct1 || !ct2) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* selPtr = static_cast<LWECiphertext*>(sel);
auto* ct1Ptr = static_cast<LWECiphertext*>(ct1);
auto* ct2Ptr = static_cast<LWECiphertext*>(ct2);
// MUX(sel, ct1, ct2) = (sel AND ct1) OR ((NOT sel) AND ct2)
auto notSel = internal->context.EvalNOT(*selPtr);
auto branch1 = internal->context.EvalBinGate(AND, *selPtr, *ct1Ptr);
auto branch2 = internal->context.EvalBinGate(AND, notSel, *ct2Ptr);
auto result = internal->context.EvalBinGate(OR, branch1, branch2);
return static_cast<TfheCiphertext>(new LWECiphertext(result));
} catch (...) {
return nullptr;
}
}
// =============================================================================
// Integer Operations
// =============================================================================
extern "C" TfheInteger tfhe_encrypt_integer(TfheContext ctx, TfheSecretKey sk, int64_t value, int bitLen) {
if (!ctx || !sk) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* skPtr = static_cast<LWEPrivateKey*>(sk);
auto* integer = new TfheIntegerInternal(bitLen);
int numBits = integer->numBits();
// Encrypt each bit
uint64_t uval = static_cast<uint64_t>(value);
for (int i = 0; i < numBits; i++) {
int bit = (uval >> i) & 1;
integer->bits[i] = internal->context.Encrypt(*skPtr, bit);
}
return static_cast<TfheInteger>(integer);
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_encrypt_integer_public(TfheContext ctx, TfhePublicKey pk, int64_t value, int bitLen) {
// BinFHE doesn't have public key encryption
(void)ctx;
(void)pk;
(void)value;
(void)bitLen;
return nullptr;
}
extern "C" int64_t tfhe_decrypt_integer(TfheContext ctx, TfheSecretKey sk, TfheInteger ct) {
if (!ctx || !sk || !ct) return 0;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* skPtr = static_cast<LWEPrivateKey*>(sk);
auto* integer = static_cast<TfheIntegerInternal*>(ct);
uint64_t result = 0;
for (size_t i = 0; i < integer->bits.size() && i < 64; i++) {
LWEPlaintext bit;
internal->context.Decrypt(*skPtr, integer->bits[i], &bit);
if (bit != 0) {
result |= (1ULL << i);
}
}
return static_cast<int64_t>(result);
} catch (...) {
return 0;
}
}
extern "C" void tfhe_integer_free(TfheInteger ct) {
if (ct) {
delete static_cast<TfheIntegerInternal*>(ct);
}
}
extern "C" TfheInteger tfhe_integer_clone(TfheInteger ct) {
if (!ct) return nullptr;
try {
auto* src = static_cast<TfheIntegerInternal*>(ct);
auto* dst = new TfheIntegerInternal(src->itype);
dst->bits = src->bits;
return static_cast<TfheInteger>(dst);
} catch (...) {
return nullptr;
}
}
extern "C" TfheIntType tfhe_integer_type(TfheInteger ct) {
if (!ct) return TFHE_UINT8;
auto* integer = static_cast<TfheIntegerInternal*>(ct);
return integer->itype;
}
// =============================================================================
// Integer Arithmetic (Full Adder / Subtractor circuits)
// =============================================================================
// Full adder: sum, carry = a + b + cin
static std::pair<LWECiphertext, LWECiphertext> fullAdder(
TfheContextInternal* ctx,
const LWECiphertext& a,
const LWECiphertext& b,
const LWECiphertext& cin
) {
// sum = a XOR b XOR cin
auto axorb = ctx->context.EvalBinGate(XOR, a, b);
auto sum = ctx->context.EvalBinGate(XOR, axorb, cin);
// carry = (a AND b) OR (cin AND (a XOR b))
auto aandb = ctx->context.EvalBinGate(AND, a, b);
auto cinandaxorb = ctx->context.EvalBinGate(AND, cin, axorb);
auto carry = ctx->context.EvalBinGate(OR, aandb, cinandaxorb);
return {sum, carry};
}
extern "C" TfheInteger tfhe_add(TfheContext ctx, TfheInteger a, TfheInteger b) {
if (!ctx || !a || !b) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* intB = static_cast<TfheIntegerInternal*>(b);
if (intA->itype != intB->itype) return nullptr;
auto* result = new TfheIntegerInternal(intA->itype);
int numBits = result->numBits();
// Initialize carry to encrypted 0
LWECiphertext carry = internal->context.Encrypt(internal->secretKey, 0);
for (int i = 0; i < numBits; i++) {
auto [sum, newCarry] = fullAdder(internal, intA->bits[i], intB->bits[i], carry);
result->bits[i] = sum;
carry = newCarry;
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_sub(TfheContext ctx, TfheInteger a, TfheInteger b) {
if (!ctx || !a || !b) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* intB = static_cast<TfheIntegerInternal*>(b);
if (intA->itype != intB->itype) return nullptr;
// a - b = a + (~b) + 1
auto* result = new TfheIntegerInternal(intA->itype);
int numBits = result->numBits();
// Initialize carry to encrypted 1 (for two's complement)
LWECiphertext carry = internal->context.Encrypt(internal->secretKey, 1);
for (int i = 0; i < numBits; i++) {
// NOT b
auto notB = internal->context.EvalNOT(intB->bits[i]);
auto [sum, newCarry] = fullAdder(internal, intA->bits[i], notB, carry);
result->bits[i] = sum;
carry = newCarry;
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_neg(TfheContext ctx, TfheInteger a) {
if (!ctx || !a) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
// -a = ~a + 1
auto* result = new TfheIntegerInternal(intA->itype);
int numBits = result->numBits();
LWECiphertext carry = internal->context.Encrypt(internal->secretKey, 1);
LWECiphertext zero = internal->context.Encrypt(internal->secretKey, 0);
for (int i = 0; i < numBits; i++) {
auto notA = internal->context.EvalNOT(intA->bits[i]);
auto [sum, newCarry] = fullAdder(internal, notA, zero, carry);
result->bits[i] = sum;
carry = newCarry;
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_add_scalar(TfheContext ctx, TfheInteger a, int64_t scalar) {
if (!ctx || !a) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* result = new TfheIntegerInternal(intA->itype);
int numBits = result->numBits();
LWECiphertext carry = internal->context.Encrypt(internal->secretKey, 0);
uint64_t uval = static_cast<uint64_t>(scalar);
for (int i = 0; i < numBits; i++) {
int bit = (uval >> i) & 1;
auto scalarBit = internal->context.Encrypt(internal->secretKey, bit);
auto [sum, newCarry] = fullAdder(internal, intA->bits[i], scalarBit, carry);
result->bits[i] = sum;
carry = newCarry;
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_sub_scalar(TfheContext ctx, TfheInteger a, int64_t scalar) {
if (!ctx || !a) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* result = new TfheIntegerInternal(intA->itype);
int numBits = result->numBits();
// a - scalar = a + (~scalar) + 1
uint64_t uval = static_cast<uint64_t>(scalar);
uint64_t notScalar = ~uval;
LWECiphertext carry = internal->context.Encrypt(internal->secretKey, 1);
for (int i = 0; i < numBits; i++) {
int bit = (notScalar >> i) & 1;
auto scalarBit = internal->context.Encrypt(internal->secretKey, bit);
auto [sum, newCarry] = fullAdder(internal, intA->bits[i], scalarBit, carry);
result->bits[i] = sum;
carry = newCarry;
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_mul_scalar(TfheContext ctx, TfheInteger a, int64_t scalar) {
if (!ctx || !a) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* result = new TfheIntegerInternal(intA->itype);
int numBits = result->numBits();
uint64_t uval = static_cast<uint64_t>(scalar);
// Initialize result to 0
for (int i = 0; i < numBits; i++) {
result->bits[i] = internal->context.Encrypt(internal->secretKey, 0);
}
// Shift-and-add multiplication
for (int i = 0; i < numBits && (uval >> i) != 0; i++) {
if ((uval >> i) & 1) {
// Add shifted a to result
LWECiphertext carry = internal->context.Encrypt(internal->secretKey, 0);
for (int j = i; j < numBits; j++) {
auto [sum, newCarry] = fullAdder(internal, result->bits[j], intA->bits[j-i], carry);
result->bits[j] = sum;
carry = newCarry;
}
}
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
// =============================================================================
// Integer Comparisons
// =============================================================================
extern "C" TfheCiphertext tfhe_eq(TfheContext ctx, TfheInteger a, TfheInteger b) {
if (!ctx || !a || !b) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* intB = static_cast<TfheIntegerInternal*>(b);
if (intA->itype != intB->itype) return nullptr;
// eq = AND of all (a[i] XNOR b[i])
LWECiphertext result = internal->context.Encrypt(internal->secretKey, 1);
for (size_t i = 0; i < intA->bits.size(); i++) {
auto xnor = internal->context.EvalBinGate(XNOR, intA->bits[i], intB->bits[i]);
result = internal->context.EvalBinGate(AND, result, xnor);
}
return static_cast<TfheCiphertext>(new LWECiphertext(result));
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_ne(TfheContext ctx, TfheInteger a, TfheInteger b) {
if (!ctx || !a || !b) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto eq = tfhe_eq(ctx, a, b);
if (!eq) return nullptr;
auto* eqPtr = static_cast<LWECiphertext*>(eq);
auto result = internal->context.EvalNOT(*eqPtr);
delete eqPtr;
return static_cast<TfheCiphertext>(new LWECiphertext(result));
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_lt(TfheContext ctx, TfheInteger a, TfheInteger b) {
if (!ctx || !a || !b) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* intB = static_cast<TfheIntegerInternal*>(b);
if (intA->itype != intB->itype) return nullptr;
// Compute a < b using bit comparison from MSB to LSB
// lt = 0, eq = 1
LWECiphertext lt = internal->context.Encrypt(internal->secretKey, 0);
LWECiphertext eq = internal->context.Encrypt(internal->secretKey, 1);
for (int i = static_cast<int>(intA->bits.size()) - 1; i >= 0; i--) {
// lt = lt OR (eq AND (NOT a[i]) AND b[i])
auto notA = internal->context.EvalNOT(intA->bits[i]);
auto notAandB = internal->context.EvalBinGate(AND, notA, intB->bits[i]);
auto eqAndNotAandB = internal->context.EvalBinGate(AND, eq, notAandB);
lt = internal->context.EvalBinGate(OR, lt, eqAndNotAandB);
// eq = eq AND (a[i] XNOR b[i])
auto xnor = internal->context.EvalBinGate(XNOR, intA->bits[i], intB->bits[i]);
eq = internal->context.EvalBinGate(AND, eq, xnor);
}
return static_cast<TfheCiphertext>(new LWECiphertext(lt));
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_le(TfheContext ctx, TfheInteger a, TfheInteger b) {
if (!ctx || !a || !b) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
// le = lt OR eq
auto lt = tfhe_lt(ctx, a, b);
auto eq = tfhe_eq(ctx, a, b);
if (!lt || !eq) {
if (lt) tfhe_ciphertext_free(lt);
if (eq) tfhe_ciphertext_free(eq);
return nullptr;
}
auto* ltPtr = static_cast<LWECiphertext*>(lt);
auto* eqPtr = static_cast<LWECiphertext*>(eq);
auto result = internal->context.EvalBinGate(OR, *ltPtr, *eqPtr);
delete ltPtr;
delete eqPtr;
return static_cast<TfheCiphertext>(new LWECiphertext(result));
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_gt(TfheContext ctx, TfheInteger a, TfheInteger b) {
return tfhe_lt(ctx, b, a);
}
extern "C" TfheCiphertext tfhe_ge(TfheContext ctx, TfheInteger a, TfheInteger b) {
return tfhe_le(ctx, b, a);
}
extern "C" TfheInteger tfhe_min(TfheContext ctx, TfheInteger a, TfheInteger b) {
if (!ctx || !a || !b) return nullptr;
try {
auto lt = tfhe_lt(ctx, a, b);
if (!lt) return nullptr;
auto result = tfhe_select(ctx, lt, a, b);
tfhe_ciphertext_free(lt);
return result;
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_max(TfheContext ctx, TfheInteger a, TfheInteger b) {
if (!ctx || !a || !b) return nullptr;
try {
auto lt = tfhe_lt(ctx, a, b);
if (!lt) return nullptr;
auto result = tfhe_select(ctx, lt, b, a);
tfhe_ciphertext_free(lt);
return result;
} catch (...) {
return nullptr;
}
}
// =============================================================================
// Integer Bitwise Operations
// =============================================================================
extern "C" TfheInteger tfhe_bitwise_and(TfheContext ctx, TfheInteger a, TfheInteger b) {
if (!ctx || !a || !b) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* intB = static_cast<TfheIntegerInternal*>(b);
if (intA->itype != intB->itype) return nullptr;
auto* result = new TfheIntegerInternal(intA->itype);
for (size_t i = 0; i < intA->bits.size(); i++) {
result->bits[i] = internal->context.EvalBinGate(AND, intA->bits[i], intB->bits[i]);
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_bitwise_or(TfheContext ctx, TfheInteger a, TfheInteger b) {
if (!ctx || !a || !b) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* intB = static_cast<TfheIntegerInternal*>(b);
if (intA->itype != intB->itype) return nullptr;
auto* result = new TfheIntegerInternal(intA->itype);
for (size_t i = 0; i < intA->bits.size(); i++) {
result->bits[i] = internal->context.EvalBinGate(OR, intA->bits[i], intB->bits[i]);
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_bitwise_xor(TfheContext ctx, TfheInteger a, TfheInteger b) {
if (!ctx || !a || !b) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* intB = static_cast<TfheIntegerInternal*>(b);
if (intA->itype != intB->itype) return nullptr;
auto* result = new TfheIntegerInternal(intA->itype);
for (size_t i = 0; i < intA->bits.size(); i++) {
result->bits[i] = internal->context.EvalBinGate(XOR, intA->bits[i], intB->bits[i]);
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_bitwise_not(TfheContext ctx, TfheInteger a) {
if (!ctx || !a) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* result = new TfheIntegerInternal(intA->itype);
for (size_t i = 0; i < intA->bits.size(); i++) {
result->bits[i] = internal->context.EvalNOT(intA->bits[i]);
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_shl(TfheContext ctx, TfheInteger a, int bits) {
if (!ctx || !a || bits < 0) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* result = new TfheIntegerInternal(intA->itype);
int numBits = result->numBits();
for (int i = 0; i < numBits; i++) {
if (i < bits) {
result->bits[i] = internal->context.Encrypt(internal->secretKey, 0);
} else {
result->bits[i] = intA->bits[i - bits];
}
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_shr(TfheContext ctx, TfheInteger a, int bits) {
if (!ctx || !a || bits < 0) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* result = new TfheIntegerInternal(intA->itype);
int numBits = result->numBits();
for (int i = 0; i < numBits; i++) {
if (i + bits >= numBits) {
result->bits[i] = internal->context.Encrypt(internal->secretKey, 0);
} else {
result->bits[i] = intA->bits[i + bits];
}
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
// =============================================================================
// Control Flow
// =============================================================================
extern "C" TfheInteger tfhe_select(TfheContext ctx, TfheCiphertext cond, TfheInteger if_true, TfheInteger if_false) {
if (!ctx || !cond || !if_true || !if_false) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* condPtr = static_cast<LWECiphertext*>(cond);
auto* intTrue = static_cast<TfheIntegerInternal*>(if_true);
auto* intFalse = static_cast<TfheIntegerInternal*>(if_false);
if (intTrue->itype != intFalse->itype) return nullptr;
auto* result = new TfheIntegerInternal(intTrue->itype);
auto notCond = internal->context.EvalNOT(*condPtr);
for (size_t i = 0; i < intTrue->bits.size(); i++) {
// result[i] = (cond AND true[i]) OR ((NOT cond) AND false[i])
auto condAndTrue = internal->context.EvalBinGate(AND, *condPtr, intTrue->bits[i]);
auto notCondAndFalse = internal->context.EvalBinGate(AND, notCond, intFalse->bits[i]);
result->bits[i] = internal->context.EvalBinGate(OR, condAndTrue, notCondAndFalse);
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_cast_to(TfheContext ctx, TfheInteger a, int target_bitlen) {
if (!ctx || !a || target_bitlen <= 0) return nullptr;
try {
auto* internal = static_cast<TfheContextInternal*>(ctx);
auto* intA = static_cast<TfheIntegerInternal*>(a);
auto* result = new TfheIntegerInternal(target_bitlen);
int srcBits = intA->numBits();
int dstBits = result->numBits();
for (int i = 0; i < dstBits; i++) {
if (i < srcBits) {
result->bits[i] = intA->bits[i];
} else {
result->bits[i] = internal->context.Encrypt(internal->secretKey, 0);
}
}
return static_cast<TfheInteger>(result);
} catch (...) {
return nullptr;
}
}
// =============================================================================
// Serialization (using OpenFHE's serialization)
// =============================================================================
extern "C" uint8_t* tfhe_serialize_ciphertext(TfheContext ctx, TfheCiphertext ct, size_t* out_len) {
if (!ctx || !ct || !out_len) return nullptr;
try {
auto* ctPtr = static_cast<LWECiphertext*>(ct);
std::stringstream ss;
// Use OpenFHE's serialization
lbcrypto::Serial::Serialize(*ctPtr, ss, lbcrypto::SerType::BINARY);
std::string data = ss.str();
*out_len = data.size();
uint8_t* out = static_cast<uint8_t*>(malloc(*out_len));
if (out) {
std::memcpy(out, data.data(), *out_len);
}
return out;
} catch (...) {
return nullptr;
}
}
extern "C" TfheCiphertext tfhe_deserialize_ciphertext(TfheContext ctx, const uint8_t* data, size_t len) {
if (!ctx || !data || len == 0) return nullptr;
try {
std::stringstream ss(std::string(reinterpret_cast<const char*>(data), len));
LWECiphertext ct;
lbcrypto::Serial::Deserialize(ct, ss, lbcrypto::SerType::BINARY);
return static_cast<TfheCiphertext>(new LWECiphertext(ct));
} catch (...) {
return nullptr;
}
}
extern "C" uint8_t* tfhe_serialize_secret_key(TfheContext ctx, TfheSecretKey sk, size_t* out_len) {
if (!ctx || !sk || !out_len) return nullptr;
try {
auto* skPtr = static_cast<LWEPrivateKey*>(sk);
std::stringstream ss;
lbcrypto::Serial::Serialize(*skPtr, ss, lbcrypto::SerType::BINARY);
std::string data = ss.str();
*out_len = data.size();
uint8_t* out = static_cast<uint8_t*>(malloc(*out_len));
if (out) {
std::memcpy(out, data.data(), *out_len);
}
return out;
} catch (...) {
return nullptr;
}
}
extern "C" TfheSecretKey tfhe_deserialize_secret_key(TfheContext ctx, const uint8_t* data, size_t len) {
if (!ctx || !data || len == 0) return nullptr;
try {
std::stringstream ss(std::string(reinterpret_cast<const char*>(data), len));
LWEPrivateKey sk;
lbcrypto::Serial::Deserialize(sk, ss, lbcrypto::SerType::BINARY);
return static_cast<TfheSecretKey>(new LWEPrivateKey(sk));
} catch (...) {
return nullptr;
}
}
extern "C" uint8_t* tfhe_serialize_public_key(TfheContext ctx, TfhePublicKey pk, size_t* out_len) {
(void)ctx;
(void)pk;
(void)out_len;
return nullptr; // Not implemented for BinFHE
}
extern "C" TfhePublicKey tfhe_deserialize_public_key(TfheContext ctx, const uint8_t* data, size_t len) {
(void)ctx;
(void)data;
(void)len;
return nullptr; // Not implemented for BinFHE
}
extern "C" uint8_t* tfhe_serialize_integer(TfheContext ctx, TfheInteger ct, size_t* out_len) {
if (!ctx || !ct || !out_len) return nullptr;
try {
auto* integer = static_cast<TfheIntegerInternal*>(ct);
std::stringstream ss;
// Write type
int32_t itype = static_cast<int32_t>(integer->itype);
ss.write(reinterpret_cast<const char*>(&itype), sizeof(itype));
// Write number of bits
uint32_t numBits = static_cast<uint32_t>(integer->bits.size());
ss.write(reinterpret_cast<const char*>(&numBits), sizeof(numBits));
// Serialize each bit
for (const auto& bit : integer->bits) {
lbcrypto::Serial::Serialize(bit, ss, lbcrypto::SerType::BINARY);
}
std::string data = ss.str();
*out_len = data.size();
uint8_t* out = static_cast<uint8_t*>(malloc(*out_len));
if (out) {
std::memcpy(out, data.data(), *out_len);
}
return out;
} catch (...) {
return nullptr;
}
}
extern "C" TfheInteger tfhe_deserialize_integer(TfheContext ctx, const uint8_t* data, size_t len) {
if (!ctx || !data || len == 0) return nullptr;
try {
std::stringstream ss(std::string(reinterpret_cast<const char*>(data), len));
// Read type
int32_t itype;
ss.read(reinterpret_cast<char*>(&itype), sizeof(itype));
// Read number of bits
uint32_t numBits;
ss.read(reinterpret_cast<char*>(&numBits), sizeof(numBits));
auto* integer = new TfheIntegerInternal(static_cast<TfheIntType>(itype));
integer->bits.resize(numBits);
// Deserialize each bit
for (uint32_t i = 0; i < numBits; i++) {
lbcrypto::Serial::Deserialize(integer->bits[i], ss, lbcrypto::SerType::BINARY);
}
return static_cast<TfheInteger>(integer);
} catch (...) {
return nullptr;
}
}