mirror of
https://github.com/luxfi/fhe.git
synced 2026-07-27 07:24:44 +00:00
169 lines
4.6 KiB
Go
169 lines
4.6 KiB
Go
// Copyright (c) 2025, Lux Industries Inc
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package fhe
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
)
|
|
|
|
// ThresholdRNGProvider is an interface for threshold randomness providers.
|
|
// Implementations typically connect to a threshold network (e.g., T-Chain)
|
|
// to generate verifiable random values.
|
|
type ThresholdRNGProvider interface {
|
|
// RequestRandomness requests random bytes from the threshold network.
|
|
RequestRandomness(ctx context.Context, numBytes int) ([]byte, error)
|
|
|
|
// IsAvailable returns true if the threshold network is reachable.
|
|
IsAvailable(ctx context.Context) bool
|
|
}
|
|
|
|
// ThresholdRNGConfig configures the threshold RNG behavior.
|
|
type ThresholdRNGConfig struct {
|
|
// Provider is the threshold randomness source.
|
|
Provider ThresholdRNGProvider
|
|
|
|
// FallbackEnabled allows local PRNG fallback when threshold network is unavailable.
|
|
FallbackEnabled bool
|
|
|
|
// MinThreshold is the minimum number of parties required for threshold operations.
|
|
MinThreshold int
|
|
}
|
|
|
|
// ThresholdRNG generates encrypted random numbers using threshold randomness.
|
|
// It combines threshold network randomness with FHE encryption.
|
|
type ThresholdRNG struct {
|
|
params Parameters
|
|
sk *SecretKey
|
|
pk *PublicKey
|
|
config *ThresholdRNGConfig
|
|
enc *BitwiseEncryptor
|
|
pubEnc *BitwisePublicEncryptor
|
|
state [32]byte
|
|
counter uint64
|
|
fallback bool
|
|
}
|
|
|
|
// NewThresholdRNG creates a new threshold RNG instance.
|
|
// If sk is provided, uses secret key encryption; otherwise uses public key encryption.
|
|
func NewThresholdRNG(params Parameters, sk *SecretKey, pk *PublicKey, config *ThresholdRNGConfig) *ThresholdRNG {
|
|
rng := &ThresholdRNG{
|
|
params: params,
|
|
sk: sk,
|
|
pk: pk,
|
|
config: config,
|
|
counter: 0,
|
|
}
|
|
|
|
if sk != nil {
|
|
rng.enc = NewBitwiseEncryptor(params, sk)
|
|
}
|
|
if pk != nil {
|
|
rng.pubEnc = NewBitwisePublicEncryptor(params, pk)
|
|
}
|
|
|
|
return rng
|
|
}
|
|
|
|
// reseed gets new randomness from the threshold network or falls back to local PRNG.
|
|
func (rng *ThresholdRNG) reseed(ctx context.Context, seed []byte) {
|
|
// Use provided seed if available
|
|
if len(seed) > 0 {
|
|
rng.state = sha256.Sum256(seed)
|
|
return
|
|
}
|
|
|
|
// Try threshold network
|
|
if rng.config != nil && rng.config.Provider != nil && rng.config.Provider.IsAvailable(ctx) {
|
|
if randomBytes, err := rng.config.Provider.RequestRandomness(ctx, 32); err == nil && len(randomBytes) >= 32 {
|
|
copy(rng.state[:], randomBytes[:32])
|
|
rng.fallback = false
|
|
return
|
|
}
|
|
}
|
|
|
|
// Fallback to counter-based state if enabled
|
|
if rng.config == nil || rng.config.FallbackEnabled {
|
|
var data [8]byte
|
|
binary.LittleEndian.PutUint64(data[:], rng.counter)
|
|
rng.state = sha256.Sum256(data[:])
|
|
rng.fallback = true
|
|
}
|
|
}
|
|
|
|
// advance advances the PRNG state
|
|
func (rng *ThresholdRNG) advance() [32]byte {
|
|
var data [40]byte
|
|
copy(data[:32], rng.state[:])
|
|
binary.LittleEndian.PutUint64(data[32:], rng.counter)
|
|
rng.counter++
|
|
rng.state = sha256.Sum256(data[:])
|
|
return rng.state
|
|
}
|
|
|
|
// RandomBit generates an encrypted random bit using threshold randomness.
|
|
func (rng *ThresholdRNG) RandomBit(ctx context.Context, seed []byte) (*Ciphertext, error) {
|
|
rng.reseed(ctx, seed)
|
|
random := rng.advance()
|
|
bit := (random[0] & 1) == 1
|
|
|
|
if rng.enc != nil {
|
|
return rng.enc.enc.Encrypt(bit), nil
|
|
}
|
|
if rng.pubEnc != nil {
|
|
return rng.pubEnc.Encrypt(bit)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// RandomUint generates an encrypted random integer using threshold randomness.
|
|
func (rng *ThresholdRNG) RandomUint(ctx context.Context, t FheUintType, seed []byte) (*BitCiphertext, error) {
|
|
rng.reseed(ctx, seed)
|
|
numBits := t.NumBits()
|
|
bits := make([]*Ciphertext, numBits)
|
|
|
|
for i := 0; i < numBits; i++ {
|
|
random := rng.advance()
|
|
bit := (random[0] & 1) == 1
|
|
if rng.enc != nil {
|
|
bits[i] = rng.enc.enc.Encrypt(bit)
|
|
} else if rng.pubEnc != nil {
|
|
ct, err := rng.pubEnc.Encrypt(bit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bits[i] = ct
|
|
}
|
|
}
|
|
|
|
return &BitCiphertext{
|
|
bits: bits,
|
|
numBits: numBits,
|
|
fheType: t,
|
|
}, nil
|
|
}
|
|
|
|
// IsThresholdAvailable checks if the threshold network is available.
|
|
func (rng *ThresholdRNG) IsThresholdAvailable(ctx context.Context) bool {
|
|
if rng.config == nil || rng.config.Provider == nil {
|
|
return false
|
|
}
|
|
return rng.config.Provider.IsAvailable(ctx)
|
|
}
|
|
|
|
// IsFallback returns true if the RNG is using fallback (local) randomness.
|
|
func (rng *ThresholdRNG) IsFallback() bool {
|
|
return rng.fallback
|
|
}
|
|
|
|
// CalculateThreshold calculates the minimum threshold for t-of-n threshold schemes.
|
|
// Uses the standard formula: t = floor(n/2) + 1 for honest majority.
|
|
func CalculateThreshold(n int) int {
|
|
if n <= 0 {
|
|
return 0
|
|
}
|
|
return n/2 + 1
|
|
}
|