mirror of
https://github.com/luxfi/accel.git
synced 2026-10-30 17:03:45 +00:00
436 lines
12 KiB
Go
436 lines
12 KiB
Go
//go:build cgo && luxfhe
|
|
|
|
// Package fhe provides GPU-accelerated FHE operations via luxfhe C++ library.
|
|
//
|
|
// When built with `go build -tags luxfhe`, this uses the high-performance
|
|
// C++ implementation. Otherwise, falls back to pure Go.
|
|
//
|
|
// Copyright (c) 2024-2025 Lux Industries Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
package fhe
|
|
|
|
/*
|
|
#cgo pkg-config: lux-fhe
|
|
#include <lux/fhe/c_api.h>
|
|
#include <stdlib.h>
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"errors"
|
|
"runtime"
|
|
"unsafe"
|
|
)
|
|
|
|
// Errors from C library
|
|
var (
|
|
ErrNullPtr = errors.New("fhe: null pointer")
|
|
ErrInvalidArg = errors.New("fhe: invalid argument")
|
|
ErrAlloc = errors.New("fhe: allocation failed")
|
|
ErrKeyGen = errors.New("fhe: key generation failed")
|
|
ErrEncrypt = errors.New("fhe: encryption failed")
|
|
ErrDecrypt = errors.New("fhe: decryption failed")
|
|
ErrBootstrap = errors.New("fhe: bootstrap failed")
|
|
ErrGate = errors.New("fhe: gate evaluation failed")
|
|
ErrSerialize = errors.New("fhe: serialization failed")
|
|
ErrDeserialize = errors.New("fhe: deserialization failed")
|
|
ErrNotInit = errors.New("fhe: not initialized")
|
|
)
|
|
|
|
func cgoErr(e C.LuxFheError) error {
|
|
switch e {
|
|
case C.LUX_FHE_OK:
|
|
return nil
|
|
case C.LUX_FHE_ERR_NULL_PTR:
|
|
return ErrNullPtr
|
|
case C.LUX_FHE_ERR_INVALID_PARAM:
|
|
return ErrInvalidArg
|
|
case C.LUX_FHE_ERR_ALLOC:
|
|
return ErrAlloc
|
|
case C.LUX_FHE_ERR_KEYGEN:
|
|
return ErrKeyGen
|
|
case C.LUX_FHE_ERR_ENCRYPT:
|
|
return ErrEncrypt
|
|
case C.LUX_FHE_ERR_DECRYPT:
|
|
return ErrDecrypt
|
|
case C.LUX_FHE_ERR_BOOTSTRAP:
|
|
return ErrBootstrap
|
|
case C.LUX_FHE_ERR_GATE:
|
|
return ErrGate
|
|
case C.LUX_FHE_ERR_SERIALIZE:
|
|
return ErrSerialize
|
|
case C.LUX_FHE_ERR_DESERIALIZE:
|
|
return ErrDeserialize
|
|
case C.LUX_FHE_ERR_NOT_INIT:
|
|
return ErrNotInit
|
|
default:
|
|
return errors.New("fhe: unknown error")
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// Parameter Sets
|
|
// =============================================================================
|
|
|
|
// CgoParams represents FHE parameter sets (C++ backend)
|
|
type CgoParams int
|
|
|
|
const (
|
|
CgoParamsToy CgoParams = C.LUX_FHE_PARAMS_TOY
|
|
CgoParamsMedium CgoParams = C.LUX_FHE_PARAMS_MEDIUM
|
|
CgoParamsSTD128 CgoParams = C.LUX_FHE_PARAMS_STD128
|
|
CgoParamsSTD128Q CgoParams = C.LUX_FHE_PARAMS_STD128Q
|
|
CgoParamsSTD192 CgoParams = C.LUX_FHE_PARAMS_STD192
|
|
CgoParamsSTD192Q CgoParams = C.LUX_FHE_PARAMS_STD192Q
|
|
CgoParamsSTD256 CgoParams = C.LUX_FHE_PARAMS_STD256
|
|
CgoParamsSTD256Q CgoParams = C.LUX_FHE_PARAMS_STD256Q
|
|
)
|
|
|
|
// CgoMethod represents bootstrapping methods
|
|
type CgoMethod int
|
|
|
|
const (
|
|
CgoMethodAP CgoMethod = C.LUX_FHE_METHOD_AP
|
|
CgoMethodGINX CgoMethod = C.LUX_FHE_METHOD_GINX
|
|
CgoMethodLMKCDEY CgoMethod = C.LUX_FHE_METHOD_LMKCDEY
|
|
)
|
|
|
|
// =============================================================================
|
|
// Context
|
|
// =============================================================================
|
|
|
|
// CgoContext wraps the C++ FHE context
|
|
type CgoContext struct {
|
|
ptr *C.LuxFheContext
|
|
}
|
|
|
|
// NewCgoContext creates a new FHE context with C++ backend
|
|
func NewCgoContext(params CgoParams, method CgoMethod) (*CgoContext, error) {
|
|
var ptr *C.LuxFheContext
|
|
err := C.lux_fhe_context_new(&ptr, C.LuxFheParams(params), C.LuxFheMethod(method))
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
ctx := &CgoContext{ptr: ptr}
|
|
runtime.SetFinalizer(ctx, (*CgoContext).Free)
|
|
return ctx, nil
|
|
}
|
|
|
|
// Free releases the context resources
|
|
func (c *CgoContext) Free() {
|
|
if c.ptr != nil {
|
|
C.lux_fhe_context_free(c.ptr)
|
|
c.ptr = nil
|
|
}
|
|
}
|
|
|
|
// N returns the LWE dimension
|
|
func (c *CgoContext) N() uint32 {
|
|
return uint32(C.lux_fhe_context_n(c.ptr))
|
|
}
|
|
|
|
// RingDim returns the ring dimension
|
|
func (c *CgoContext) RingDim() uint32 {
|
|
return uint32(C.lux_fhe_context_ring_dim(c.ptr))
|
|
}
|
|
|
|
// =============================================================================
|
|
// Keys
|
|
// =============================================================================
|
|
|
|
// CgoSecretKey wraps C++ secret key
|
|
type CgoSecretKey struct {
|
|
ptr *C.LuxFheSecretKey
|
|
}
|
|
|
|
// CgoPublicKey wraps C++ public key
|
|
type CgoPublicKey struct {
|
|
ptr *C.LuxFhePublicKey
|
|
}
|
|
|
|
// CgoBootstrapKey wraps C++ bootstrap key
|
|
type CgoBootstrapKey struct {
|
|
ptr *C.LuxFheBootstrapKey
|
|
}
|
|
|
|
// GenSecretKey generates a secret key
|
|
func (c *CgoContext) GenSecretKey() (*CgoSecretKey, error) {
|
|
var ptr *C.LuxFheSecretKey
|
|
err := C.lux_fhe_keygen_secret(c.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
sk := &CgoSecretKey{ptr: ptr}
|
|
runtime.SetFinalizer(sk, (*CgoSecretKey).Free)
|
|
return sk, nil
|
|
}
|
|
|
|
// GenPublicKey generates a public key from secret key
|
|
func (c *CgoContext) GenPublicKey(sk *CgoSecretKey) (*CgoPublicKey, error) {
|
|
var ptr *C.LuxFhePublicKey
|
|
err := C.lux_fhe_keygen_public(c.ptr, sk.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
pk := &CgoPublicKey{ptr: ptr}
|
|
runtime.SetFinalizer(pk, (*CgoPublicKey).Free)
|
|
return pk, nil
|
|
}
|
|
|
|
// GenBootstrapKey generates bootstrap key for homomorphic gates
|
|
func (c *CgoContext) GenBootstrapKey(sk *CgoSecretKey) (*CgoBootstrapKey, error) {
|
|
var ptr *C.LuxFheBootstrapKey
|
|
err := C.lux_fhe_keygen_bootstrap(c.ptr, sk.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
bsk := &CgoBootstrapKey{ptr: ptr}
|
|
runtime.SetFinalizer(bsk, (*CgoBootstrapKey).Free)
|
|
return bsk, nil
|
|
}
|
|
|
|
func (sk *CgoSecretKey) Free() {
|
|
if sk.ptr != nil {
|
|
C.lux_fhe_secretkey_free(sk.ptr)
|
|
sk.ptr = nil
|
|
}
|
|
}
|
|
|
|
func (pk *CgoPublicKey) Free() {
|
|
if pk.ptr != nil {
|
|
C.lux_fhe_publickey_free(pk.ptr)
|
|
pk.ptr = nil
|
|
}
|
|
}
|
|
|
|
func (bsk *CgoBootstrapKey) Free() {
|
|
if bsk.ptr != nil {
|
|
C.lux_fhe_bootstrapkey_free(bsk.ptr)
|
|
bsk.ptr = nil
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// Ciphertext
|
|
// =============================================================================
|
|
|
|
// CgoCiphertext wraps C++ ciphertext
|
|
type CgoCiphertext struct {
|
|
ptr *C.LuxFheCiphertext
|
|
}
|
|
|
|
// Encrypt encrypts a bit using secret key
|
|
func (c *CgoContext) Encrypt(sk *CgoSecretKey, plaintext bool) (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_encrypt(c.ptr, sk.ptr, C.bool(plaintext), &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
ct := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(ct, (*CgoCiphertext).Free)
|
|
return ct, nil
|
|
}
|
|
|
|
// EncryptPK encrypts a bit using public key
|
|
func (c *CgoContext) EncryptPK(pk *CgoPublicKey, plaintext bool) (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_encrypt_pk(c.ptr, pk.ptr, C.bool(plaintext), &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
ct := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(ct, (*CgoCiphertext).Free)
|
|
return ct, nil
|
|
}
|
|
|
|
// Decrypt decrypts a ciphertext
|
|
func (c *CgoContext) Decrypt(sk *CgoSecretKey, ct *CgoCiphertext) (bool, error) {
|
|
var result C.bool
|
|
err := C.lux_fhe_decrypt(c.ptr, sk.ptr, ct.ptr, &result)
|
|
if err != C.LUX_FHE_OK {
|
|
return false, cgoErr(err)
|
|
}
|
|
return bool(result), nil
|
|
}
|
|
|
|
func (ct *CgoCiphertext) Free() {
|
|
if ct.ptr != nil {
|
|
C.lux_fhe_ciphertext_free(ct.ptr)
|
|
ct.ptr = nil
|
|
}
|
|
}
|
|
|
|
// Clone creates a copy of the ciphertext
|
|
func (ct *CgoCiphertext) Clone() (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_ciphertext_clone(ct.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
clone := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(clone, (*CgoCiphertext).Free)
|
|
return clone, nil
|
|
}
|
|
|
|
// =============================================================================
|
|
// Gates
|
|
// =============================================================================
|
|
|
|
// Not evaluates NOT gate (no bootstrapping needed)
|
|
func (c *CgoContext) Not(ct *CgoCiphertext) (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_not(c.ptr, ct.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
result := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(result, (*CgoCiphertext).Free)
|
|
return result, nil
|
|
}
|
|
|
|
// And evaluates AND gate with bootstrapping
|
|
func (c *CgoContext) And(bsk *CgoBootstrapKey, a, b *CgoCiphertext) (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_and(c.ptr, bsk.ptr, a.ptr, b.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
result := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(result, (*CgoCiphertext).Free)
|
|
return result, nil
|
|
}
|
|
|
|
// Or evaluates OR gate with bootstrapping
|
|
func (c *CgoContext) Or(bsk *CgoBootstrapKey, a, b *CgoCiphertext) (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_or(c.ptr, bsk.ptr, a.ptr, b.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
result := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(result, (*CgoCiphertext).Free)
|
|
return result, nil
|
|
}
|
|
|
|
// Xor evaluates XOR gate with bootstrapping
|
|
func (c *CgoContext) Xor(bsk *CgoBootstrapKey, a, b *CgoCiphertext) (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_xor(c.ptr, bsk.ptr, a.ptr, b.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
result := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(result, (*CgoCiphertext).Free)
|
|
return result, nil
|
|
}
|
|
|
|
// Nand evaluates NAND gate with bootstrapping
|
|
func (c *CgoContext) Nand(bsk *CgoBootstrapKey, a, b *CgoCiphertext) (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_nand(c.ptr, bsk.ptr, a.ptr, b.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
result := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(result, (*CgoCiphertext).Free)
|
|
return result, nil
|
|
}
|
|
|
|
// Nor evaluates NOR gate with bootstrapping
|
|
func (c *CgoContext) Nor(bsk *CgoBootstrapKey, a, b *CgoCiphertext) (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_nor(c.ptr, bsk.ptr, a.ptr, b.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
result := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(result, (*CgoCiphertext).Free)
|
|
return result, nil
|
|
}
|
|
|
|
// Xnor evaluates XNOR gate with bootstrapping
|
|
func (c *CgoContext) Xnor(bsk *CgoBootstrapKey, a, b *CgoCiphertext) (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_xnor(c.ptr, bsk.ptr, a.ptr, b.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
result := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(result, (*CgoCiphertext).Free)
|
|
return result, nil
|
|
}
|
|
|
|
// Mux evaluates MUX gate (if sel then a else b)
|
|
func (c *CgoContext) Mux(bsk *CgoBootstrapKey, sel, a, b *CgoCiphertext) (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_mux(c.ptr, bsk.ptr, sel.ptr, a.ptr, b.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
result := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(result, (*CgoCiphertext).Free)
|
|
return result, nil
|
|
}
|
|
|
|
// Bootstrap refreshes ciphertext to reduce noise
|
|
func (c *CgoContext) Bootstrap(bsk *CgoBootstrapKey, ct *CgoCiphertext) (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_bootstrap(c.ptr, bsk.ptr, ct.ptr, &ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
result := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(result, (*CgoCiphertext).Free)
|
|
return result, nil
|
|
}
|
|
|
|
// =============================================================================
|
|
// Serialization
|
|
// =============================================================================
|
|
|
|
// Marshal serializes ciphertext to bytes
|
|
func (ct *CgoCiphertext) Marshal() ([]byte, error) {
|
|
var data *C.uint8_t
|
|
var length C.size_t
|
|
err := C.lux_fhe_ciphertext_marshal(ct.ptr, &data, &length)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
defer C.lux_fhe_bytes_free(data)
|
|
return C.GoBytes(unsafe.Pointer(data), C.int(length)), nil
|
|
}
|
|
|
|
// UnmarshalCiphertext deserializes ciphertext from bytes
|
|
func (c *CgoContext) UnmarshalCiphertext(data []byte) (*CgoCiphertext, error) {
|
|
var ptr *C.LuxFheCiphertext
|
|
err := C.lux_fhe_ciphertext_unmarshal(c.ptr,
|
|
(*C.uint8_t)(unsafe.Pointer(&data[0])),
|
|
C.size_t(len(data)),
|
|
&ptr)
|
|
if err != C.LUX_FHE_OK {
|
|
return nil, cgoErr(err)
|
|
}
|
|
ct := &CgoCiphertext{ptr: ptr}
|
|
runtime.SetFinalizer(ct, (*CgoCiphertext).Free)
|
|
return ct, nil
|
|
}
|
|
|
|
// =============================================================================
|
|
// Utility
|
|
// =============================================================================
|
|
|
|
// Version returns the C++ library version
|
|
func Version() string {
|
|
return C.GoString(C.lux_fhe_version())
|
|
}
|
|
|
|
// HasGPU returns true if GPU acceleration is available
|
|
func HasGPU() bool {
|
|
return bool(C.lux_fhe_has_gpu())
|
|
}
|
|
|
|
// IsCgoEnabled returns true (this file is only compiled with cgo)
|
|
func IsCgoEnabled() bool {
|
|
return true
|
|
}
|