mirror of
https://github.com/luxfi/fhe.git
synced 2026-07-26 23:16:08 +00:00
security: fix FHE encryptor panic and NTT timing side-channel
F13: Add EncryptSafe(bool) (*Ciphertext, error) that returns errors instead of panicking. Existing Encrypt(bool) is preserved but deprecated -- it now calls EncryptSafe internally. F07: Remove data-dependent branch (if hi == 0) from mulModBarrett. Always execute the full 128-bit reduction path to eliminate timing side-channel.
This commit is contained in:
+17
-5
@@ -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
|
||||
|
||||
+5
-7
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user