diff --git a/encryptor.go b/encryptor.go index 61d8397..4ecc4e6 100644 --- a/encryptor.go +++ b/encryptor.go @@ -4,6 +4,8 @@ package fhe import ( + "fmt" + "github.com/luxfi/lattice/v7/core/rlwe" ) @@ -23,9 +25,9 @@ func NewEncryptor(params Parameters, sk *SecretKey) *Encryptor { } } -// Encrypt encrypts a boolean value -// Note: Panics on error (should not happen with valid parameters) -func (enc *Encryptor) Encrypt(value bool) *Ciphertext { +// EncryptSafe encrypts a boolean value, returning an error on failure. +// Prefer this over Encrypt for production code paths. +func (enc *Encryptor) EncryptSafe(value bool) (*Ciphertext, error) { pt := rlwe.NewPlaintext(enc.params.paramsLWE, enc.params.paramsLWE.MaxLevel()) q := enc.params.QLWE() @@ -46,10 +48,20 @@ func (enc *Encryptor) Encrypt(value bool) *Ciphertext { ct := rlwe.NewCiphertext(enc.params.paramsLWE, 1, enc.params.paramsLWE.MaxLevel()) if err := enc.encryptor.Encrypt(pt, ct); err != nil { - panic(err) // Should not happen with valid parameters + return nil, fmt.Errorf("fhe encrypt: %w", err) } - return &Ciphertext{ct} + return &Ciphertext{ct}, nil +} + +// Encrypt encrypts a boolean value. +// Deprecated: Use EncryptSafe for production code. This method panics on error. +func (enc *Encryptor) Encrypt(value bool) *Ciphertext { + ct, err := enc.EncryptSafe(value) + if err != nil { + panic(err) + } + return ct } // EncryptBit is an alias for Encrypt diff --git a/ntt_simd.go b/ntt_simd.go index 81205b1..f6b5dec 100644 --- a/ntt_simd.go +++ b/ntt_simd.go @@ -333,15 +333,13 @@ func (e *NTTEngine) mulMod(a, b uint64) uint64 { return div128(hi, lo, e.Q) } -// mulModBarrett computes (a * b) mod Q using Barrett reduction -// For Q up to ~58 bits, we use a two-word Barrett with mu = floor(2^(2k)/Q) -// where k = number of bits in Q. This gives enough precision for the quotient. +// mulModBarrett computes (a * b) mod Q using Barrett reduction. +// Constant-time: always executes the full 128-bit path regardless of input. func (e *NTTEngine) mulModBarrett(a, b uint64) uint64 { hi, lo := mul64(a, b) - if hi == 0 { - return lo % e.Q - } - return div128(hi, lo, e.Q) + // Always execute the full path to avoid timing side-channel on hi == 0 + _, r := bits.Div64(hi%e.Q, lo, e.Q) + return r } // mul64 multiplies two 64-bit integers and returns 128-bit result as (hi, lo)