Files
fhe/serialization.go
T
Zach Kelling 1ed73f84d7 fix: restore FHE Go implementation files
Restore core FHE Go files that were removed during cleanup.
These are required for precompile/fhe to build.
2026-01-03 19:11:38 -08:00

337 lines
8.2 KiB
Go

// Copyright (c) 2025, Lux Industries Inc
// SPDX-License-Identifier: BSD-3-Clause
package fhe
import (
"bytes"
"encoding/binary"
"encoding/gob"
"fmt"
"io"
"github.com/luxfi/lattice/v7/core/rlwe"
"github.com/luxfi/lattice/v7/ring"
)
// ========== Secret Key Serialization ==========
// MarshalBinary serializes the secret key to binary format
func (sk *SecretKey) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
// Serialize SKLWE
if err := serializeSecretKey(&buf, sk.SKLWE); err != nil {
return nil, fmt.Errorf("serialize SKLWE: %w", err)
}
// Serialize SKBR
if err := serializeSecretKey(&buf, sk.SKBR); err != nil {
return nil, fmt.Errorf("serialize SKBR: %w", err)
}
return buf.Bytes(), nil
}
// UnmarshalBinary deserializes the secret key from binary format
func (sk *SecretKey) UnmarshalBinary(data []byte) error {
buf := bytes.NewReader(data)
// Deserialize SKLWE
sklwe, err := deserializeSecretKey(buf)
if err != nil {
return fmt.Errorf("deserialize SKLWE: %w", err)
}
sk.SKLWE = sklwe
// Deserialize SKBR
skbr, err := deserializeSecretKey(buf)
if err != nil {
return fmt.Errorf("deserialize SKBR: %w", err)
}
sk.SKBR = skbr
return nil
}
func serializeSecretKey(w io.Writer, sk *rlwe.SecretKey) error {
enc := gob.NewEncoder(w)
return enc.Encode(sk)
}
func deserializeSecretKey(r io.Reader) (*rlwe.SecretKey, error) {
dec := gob.NewDecoder(r)
var sk rlwe.SecretKey
if err := dec.Decode(&sk); err != nil {
return nil, err
}
return &sk, nil
}
// ========== Public Key Serialization ==========
// MarshalBinary serializes the public key to binary format
func (pk *PublicKey) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
// Serialize PKLWE using gob
enc := gob.NewEncoder(&buf)
if err := enc.Encode(pk.PKLWE); err != nil {
return nil, fmt.Errorf("serialize PKLWE: %w", err)
}
return buf.Bytes(), nil
}
// UnmarshalBinary deserializes the public key from binary format
func (pk *PublicKey) UnmarshalBinary(data []byte) error {
buf := bytes.NewReader(data)
dec := gob.NewDecoder(buf)
var pklwe rlwe.PublicKey
if err := dec.Decode(&pklwe); err != nil {
return fmt.Errorf("deserialize PKLWE: %w", err)
}
pk.PKLWE = &pklwe
return nil
}
// ========== Bootstrap Key Serialization ==========
// BootstrapKeyData holds serializable bootstrap key data
type BootstrapKeyData struct {
BRKData []byte
TestPolyAND []byte
TestPolyOR []byte
TestPolyNAND []byte
TestPolyNOR []byte
}
// MarshalBinary serializes the bootstrap key to binary format
func (bsk *BootstrapKey) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
// Serialize BRK using gob
enc := gob.NewEncoder(&buf)
if err := enc.Encode(bsk.BRK); err != nil {
return nil, fmt.Errorf("serialize BRK: %w", err)
}
// Serialize test polynomials
if err := serializePoly(&buf, bsk.TestPolyAND); err != nil {
return nil, fmt.Errorf("serialize TestPolyAND: %w", err)
}
if err := serializePoly(&buf, bsk.TestPolyOR); err != nil {
return nil, fmt.Errorf("serialize TestPolyOR: %w", err)
}
if err := serializePoly(&buf, bsk.TestPolyNAND); err != nil {
return nil, fmt.Errorf("serialize TestPolyNAND: %w", err)
}
if err := serializePoly(&buf, bsk.TestPolyNOR); err != nil {
return nil, fmt.Errorf("serialize TestPolyNOR: %w", err)
}
return buf.Bytes(), nil
}
// UnmarshalBinary deserializes the bootstrap key from binary format
func (bsk *BootstrapKey) UnmarshalBinary(data []byte) error {
buf := bytes.NewReader(data)
// Deserialize BRK
dec := gob.NewDecoder(buf)
if err := dec.Decode(&bsk.BRK); err != nil {
return fmt.Errorf("deserialize BRK: %w", err)
}
// Deserialize test polynomials
var err error
bsk.TestPolyAND, err = deserializePoly(buf)
if err != nil {
return fmt.Errorf("deserialize TestPolyAND: %w", err)
}
bsk.TestPolyOR, err = deserializePoly(buf)
if err != nil {
return fmt.Errorf("deserialize TestPolyOR: %w", err)
}
bsk.TestPolyNAND, err = deserializePoly(buf)
if err != nil {
return fmt.Errorf("deserialize TestPolyNAND: %w", err)
}
bsk.TestPolyNOR, err = deserializePoly(buf)
if err != nil {
return fmt.Errorf("deserialize TestPolyNOR: %w", err)
}
return nil
}
func serializePoly(w io.Writer, poly *ring.Poly) error {
// Write number of levels
numLevels := len(poly.Coeffs)
if err := binary.Write(w, binary.LittleEndian, uint32(numLevels)); err != nil {
return err
}
for _, coeffs := range poly.Coeffs {
// Write number of coefficients
if err := binary.Write(w, binary.LittleEndian, uint32(len(coeffs))); err != nil {
return err
}
// Write coefficients
for _, c := range coeffs {
if err := binary.Write(w, binary.LittleEndian, c); err != nil {
return err
}
}
}
return nil
}
func deserializePoly(r io.Reader) (*ring.Poly, error) {
var numLevels uint32
if err := binary.Read(r, binary.LittleEndian, &numLevels); err != nil {
return nil, err
}
coeffs := make([][]uint64, numLevels)
for i := range coeffs {
var numCoeffs uint32
if err := binary.Read(r, binary.LittleEndian, &numCoeffs); err != nil {
return nil, err
}
coeffs[i] = make([]uint64, numCoeffs)
for j := range coeffs[i] {
if err := binary.Read(r, binary.LittleEndian, &coeffs[i][j]); err != nil {
return nil, err
}
}
}
return &ring.Poly{Coeffs: coeffs}, nil
}
// ========== Ciphertext Serialization ==========
// MarshalBinary serializes a ciphertext to binary format
func (ct *Ciphertext) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
if err := enc.Encode(ct.Ciphertext); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// UnmarshalBinary deserializes a ciphertext from binary format
func (ct *Ciphertext) UnmarshalBinary(data []byte) error {
dec := gob.NewDecoder(bytes.NewReader(data))
ct.Ciphertext = new(rlwe.Ciphertext)
return dec.Decode(ct.Ciphertext)
}
// ========== BitCiphertext Serialization ==========
// MarshalBinary serializes a BitCiphertext to binary format
func (bc *BitCiphertext) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
// Write metadata
if err := binary.Write(&buf, binary.LittleEndian, uint32(bc.numBits)); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.LittleEndian, uint8(bc.fheType)); err != nil {
return nil, err
}
// Write each bit ciphertext
for i, bit := range bc.bits {
bitData, err := bit.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("bit %d: %w", i, err)
}
// Write length prefix
if err := binary.Write(&buf, binary.LittleEndian, uint32(len(bitData))); err != nil {
return nil, err
}
if _, err := buf.Write(bitData); err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
// UnmarshalBinary deserializes a BitCiphertext from binary format
func (bc *BitCiphertext) UnmarshalBinary(data []byte) error {
buf := bytes.NewReader(data)
// Read metadata
var numBits uint32
if err := binary.Read(buf, binary.LittleEndian, &numBits); err != nil {
return err
}
bc.numBits = int(numBits)
var fheType uint8
if err := binary.Read(buf, binary.LittleEndian, &fheType); err != nil {
return err
}
bc.fheType = FheUintType(fheType)
// Read each bit ciphertext
bc.bits = make([]*Ciphertext, bc.numBits)
for i := 0; i < bc.numBits; i++ {
var bitLen uint32
if err := binary.Read(buf, binary.LittleEndian, &bitLen); err != nil {
return err
}
bitData := make([]byte, bitLen)
if _, err := io.ReadFull(buf, bitData); err != nil {
return err
}
bc.bits[i] = new(Ciphertext)
if err := bc.bits[i].UnmarshalBinary(bitData); err != nil {
return fmt.Errorf("bit %d: %w", i, err)
}
}
return nil
}
// ========== Compact Serialization for Network Transfer ==========
// CompactCiphertext is a space-efficient representation for network transfer
type CompactCiphertext struct {
Data []byte
NumBits int
Type FheUintType
}
// ToCompact converts a BitCiphertext to a compact format
func (bc *BitCiphertext) ToCompact() (*CompactCiphertext, error) {
data, err := bc.MarshalBinary()
if err != nil {
return nil, err
}
return &CompactCiphertext{
Data: data,
NumBits: bc.numBits,
Type: bc.fheType,
}, nil
}
// FromCompact creates a BitCiphertext from compact format
func FromCompact(cc *CompactCiphertext) (*BitCiphertext, error) {
bc := new(BitCiphertext)
if err := bc.UnmarshalBinary(cc.Data); err != nil {
return nil, err
}
return bc, nil
}