mirror of
https://github.com/hanzoai/base.git
synced 2026-10-21 03:41:50 +00:00
109 lines
3.7 KiB
Go
109 lines
3.7 KiB
Go
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
//go:build fhe
|
|
|
|
// fheCRDT backend — ciphertexts that can be merged server-side.
|
|
//
|
|
// Behind the `fhe` build tag so the default binary has no TFHE
|
|
// dependency:
|
|
//
|
|
// go build -tags fhe ./...
|
|
//
|
|
// Status: research-grade. Only LWW-Register is implemented; the
|
|
// pipeline delivers <1 merge/sec and 136 KB per LWW ciphertext at
|
|
// production parameters. See audits/2026-04-12-fhecrdt-audit.pdf
|
|
// and docs/CRDT-PRIVACY-MODELS.md.
|
|
//
|
|
// Use age for production. Use fhe only where a third party must
|
|
// merge ciphertexts without holding the key — sealed auctions,
|
|
// cross-tenant aggregation — and the workload budget is measured
|
|
// in minutes per operation.
|
|
//
|
|
// Delegates to luxfi/fhe for TFHE key material and homomorphic
|
|
// gate circuits.
|
|
|
|
package crdt
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/luxfi/fhe"
|
|
)
|
|
|
|
// FHEPrivacy is the homomorphic-merge backend.
|
|
type FHEPrivacy struct {
|
|
ctx *fhe.Context // holds pk + eval keys
|
|
sk *fhe.SecretKey
|
|
}
|
|
|
|
// NewFHEPrivacy returns a backend bound to an existing FHE context.
|
|
// The secret key is required for EncryptOp and DecryptOp; a relay that
|
|
// only ever calls HomomorphicMerge can be constructed by passing a nil
|
|
// sk and a context initialised with only evaluation keys.
|
|
func NewFHEPrivacy(ctx *fhe.Context, sk *fhe.SecretKey) (*FHEPrivacy, error) {
|
|
if ctx == nil {
|
|
return nil, fmt.Errorf("crdt/privacy_fhe: context must not be nil")
|
|
}
|
|
return &FHEPrivacy{ctx: ctx, sk: sk}, nil
|
|
}
|
|
|
|
// Name returns the backend's wire tag.
|
|
func (*FHEPrivacy) Name() string { return "fhe/tfhe-v1" }
|
|
|
|
// SupportsHomomorphicMerge is true — that's the entire point.
|
|
func (*FHEPrivacy) SupportsHomomorphicMerge() bool { return true }
|
|
|
|
// EncryptOp serialises the op, encrypts each numeric field under TFHE,
|
|
// and wraps the result as an opaque blob.
|
|
//
|
|
// Scalar fields (counters, timestamps, LWW registers) are encrypted
|
|
// individually so HomomorphicMerge can touch them without decoding
|
|
// the full blob. Non-numeric fields (strings, blobs) are stored in
|
|
// an age-like seal alongside the homomorphic ciphertexts.
|
|
func (p *FHEPrivacy) EncryptOp(op Operation) ([]byte, error) {
|
|
if p.sk == nil {
|
|
return nil, fmt.Errorf("crdt/privacy_fhe: encrypt requires secret key")
|
|
}
|
|
payload, err := json.Marshal(op)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("crdt/privacy_fhe: marshal op: %w", err)
|
|
}
|
|
// See audit doc and fhe/examples/encrypted-crdt/ for the field-wise
|
|
// encryption scheme. This scaffolding holds the signature so callers
|
|
// compile today; the full op-schema-aware encoder lands with the
|
|
// scientist sweep deliverables.
|
|
return p.ctx.EncryptBytes(p.sk, payload)
|
|
}
|
|
|
|
// DecryptOp reverses EncryptOp. Requires the secret key.
|
|
func (p *FHEPrivacy) DecryptOp(blob []byte) (Operation, error) {
|
|
var op Operation
|
|
if p.sk == nil {
|
|
return op, fmt.Errorf("crdt/privacy_fhe: decrypt requires secret key")
|
|
}
|
|
payload, err := p.ctx.DecryptBytes(p.sk, blob)
|
|
if err != nil {
|
|
return op, fmt.Errorf("crdt/privacy_fhe: decrypt: %w", err)
|
|
}
|
|
if err := json.Unmarshal(payload, &op); err != nil {
|
|
return op, fmt.Errorf("crdt/privacy_fhe: unmarshal op: %w", err)
|
|
}
|
|
return op, nil
|
|
}
|
|
|
|
// HomomorphicMerge folds two encrypted op blobs into one, without
|
|
// decryption. The merge semantics match the underlying CRDT's
|
|
// join-semilattice operation: max for counters, LWW compare for
|
|
// registers, union for sets.
|
|
//
|
|
// A relay only needs `p.ctx` (evaluation keys); it should be built
|
|
// with `sk = nil`. Secret keys stay on the replicas.
|
|
func (p *FHEPrivacy) HomomorphicMerge(a, b []byte) ([]byte, error) {
|
|
return p.ctx.Merge(a, b)
|
|
}
|
|
|
|
// Compile-time check.
|
|
var _ Privacy = (*FHEPrivacy)(nil)
|