Files
zeekay eaf864890e feat: release v7.0.0 with GPU acceleration and module update
- Update module path to github.com/luxfi/lattice/v7
- Add GPU acceleration with Metal (macOS) and CUDA backends
- Enhance threshold FHE operations
- Revert to vanilla Apache 2.0 license (remove tfhe)
- Update Go version to 1.25.5
- Merge upstream lattigo improvements
2025-12-30 15:37:37 -08:00

122 lines
3.6 KiB
Go

package rgsw
import (
"github.com/luxfi/lattice/v7/core/rlwe"
"github.com/luxfi/lattice/v7/ring"
"github.com/luxfi/lattice/v7/ring/ringqp"
)
// Encryptor is a type for encrypting RGSW ciphertexts. It implements the [rlwe.Encryptor]
// interface overriding the [rlwe.Encryptor.Encrypt] and [rlwe.Encryptor.EncryptZero] methods to accept [rgsw.Ciphertext]
// types in addition to ciphertexts types in the rlwe package.
type Encryptor struct {
*rlwe.Encryptor
pool *rlwe.BufferPool
}
// NewEncryptor creates a new Encryptor type. Note that only secret-key encryption is
// supported at the moment.
func NewEncryptor(params rlwe.ParameterProvider, key rlwe.EncryptionKey) *Encryptor {
return &Encryptor{rlwe.NewEncryptor(params, key), rlwe.NewPool(params.GetRLWEParameters().RingQP())}
}
// Encrypt encrypts a plaintext pt into a ciphertext ct, which can be a [rgsw.Ciphertext]
// or any of the `rlwe` cipheretxt types.
func (enc Encryptor) Encrypt(pt *rlwe.Plaintext, ct interface{}) (err error) {
var rgswCt *Ciphertext
var isRGSW bool
if rgswCt, isRGSW = ct.(*Ciphertext); !isRGSW {
return enc.Encryptor.Encrypt(pt, ct)
}
if err = enc.EncryptZero(rgswCt); err != nil {
return
}
params := enc.GetRLWEParameters()
levelQ := rgswCt.LevelQ()
ringQ := params.RingQ().AtLevel(levelQ)
if pt != nil {
buffQP := enc.pool.GetBuffPolyQP()
defer enc.pool.RecycleBuffPolyQP(buffQP)
if !pt.IsNTT {
ringQ.NTT(pt.Value, buffQP.Q)
if !pt.IsMontgomery {
ringQ.MForm(buffQP.Q, buffQP.Q)
}
} else {
if !pt.IsMontgomery {
ringQ.MForm(pt.Value, buffQP.Q)
} else {
pt.Value.CopyLvl(levelQ, buffQP.Q)
}
}
if err := rlwe.AddPolyTimesGadgetVectorToGadgetCiphertext(
buffQP.Q,
[]rlwe.GadgetCiphertext{rgswCt.Value[0], rgswCt.Value[1]},
*params.RingQP(),
buffQP.Q); err != nil {
// Sanity check, this error should not happen.
panic(err)
}
}
return nil
}
// EncryptZero generates an encryption of zero into a ciphertext ct, which can be a [rgsw.Ciphertext]
// or any of the `rlwe` ciphertext types.
func (enc Encryptor) EncryptZero(ct interface{}) (err error) {
var rgswCt *Ciphertext
var isRGSW bool
if rgswCt, isRGSW = ct.(*Ciphertext); !isRGSW {
return enc.Encryptor.EncryptZero(rgswCt)
}
BaseRNSDecompositionVectorSize := rgswCt.Value[0].BaseRNSDecompositionVectorSize()
BaseTwoDecompositionVectorSize := rgswCt.Value[0].BaseTwoDecompositionVectorSize()
metadata := &rlwe.MetaData{}
metadata.IsMontgomery = true
metadata.IsNTT = true
for i := 0; i < BaseRNSDecompositionVectorSize; i++ {
for j := 0; j < BaseTwoDecompositionVectorSize[i]; j++ {
// extract the RingQ polynomial in case not P moduli have been provided
if rgswCt.LevelP() == -1 {
q0 := make([]ring.Poly, len(rgswCt.Value[0].Value[i][j]))
q1 := make([]ring.Poly, len(rgswCt.Value[1].Value[i][j]))
for k := range q0 {
q0[k] = rgswCt.Value[0].Value[i][j][k].Q
q1[k] = rgswCt.Value[1].Value[i][j][k].Q
}
if err = enc.Encryptor.EncryptZero(&rlwe.Ciphertext{Element: rlwe.Element[ring.Poly]{MetaData: metadata, Value: q0}}); err != nil {
return
}
if err = enc.Encryptor.EncryptZero(&rlwe.Ciphertext{Element: rlwe.Element[ring.Poly]{MetaData: metadata, Value: q1}}); err != nil {
return
}
} else {
if err = enc.Encryptor.EncryptZero(rlwe.Element[ringqp.Poly]{MetaData: metadata, Value: []ringqp.Poly(rgswCt.Value[0].Value[i][j])}); err != nil {
return
}
if err = enc.Encryptor.EncryptZero(rlwe.Element[ringqp.Poly]{MetaData: metadata, Value: []ringqp.Poly(rgswCt.Value[1].Value[i][j])}); err != nil {
return
}
}
}
}
return nil
}