// 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 }