mirror of
https://github.com/luxfi/lattice.git
synced 2026-09-24 00:25:44 +00:00
284 lines
9.5 KiB
Go
284 lines
9.5 KiB
Go
package mhefloat
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/luxdefi/lattice/v5/he/hefloat"
|
|
"github.com/luxdefi/lattice/v5/mhe"
|
|
"github.com/luxdefi/lattice/v5/ring"
|
|
|
|
"github.com/luxdefi/lattice/v5/core/rlwe"
|
|
"github.com/luxdefi/lattice/v5/utils"
|
|
"github.com/luxdefi/lattice/v5/utils/bignum"
|
|
"github.com/luxdefi/lattice/v5/utils/sampling"
|
|
)
|
|
|
|
// EncToShareProtocol is the structure storing the parameters and temporary buffers
|
|
// required by the encryption-to-shares protocol.
|
|
type EncToShareProtocol struct {
|
|
mhe.KeySwitchProtocol
|
|
|
|
params hefloat.Parameters
|
|
zero *rlwe.SecretKey
|
|
maskBigint []*big.Int
|
|
buff ring.Poly
|
|
}
|
|
|
|
func NewAdditiveShare(params hefloat.Parameters, logSlots int) mhe.AdditiveShareBigint {
|
|
|
|
nValues := 1 << logSlots
|
|
if params.RingType() == ring.Standard {
|
|
nValues <<= 1
|
|
}
|
|
|
|
return mhe.NewAdditiveShareBigint(nValues)
|
|
}
|
|
|
|
// ShallowCopy creates a shallow copy of EncToShareProtocol in which all the read-only data-structures are
|
|
// shared with the receiver and the temporary buffers are reallocated. The receiver and the returned
|
|
// EncToShareProtocol can be used concurrently.
|
|
func (e2s EncToShareProtocol) ShallowCopy() EncToShareProtocol {
|
|
|
|
maskBigint := make([]*big.Int, len(e2s.maskBigint))
|
|
for i := range maskBigint {
|
|
maskBigint[i] = new(big.Int)
|
|
}
|
|
|
|
return EncToShareProtocol{
|
|
KeySwitchProtocol: e2s.KeySwitchProtocol.ShallowCopy(),
|
|
params: e2s.params,
|
|
zero: e2s.zero,
|
|
maskBigint: maskBigint,
|
|
buff: e2s.params.RingQ().NewPoly(),
|
|
}
|
|
}
|
|
|
|
// NewEncToShareProtocol creates a new EncToShareProtocol struct from the passed parameters.
|
|
func NewEncToShareProtocol(params hefloat.Parameters, noise ring.DistributionParameters) (EncToShareProtocol, error) {
|
|
e2s := EncToShareProtocol{}
|
|
|
|
var err error
|
|
if e2s.KeySwitchProtocol, err = mhe.NewKeySwitchProtocol(params.Parameters, noise); err != nil {
|
|
return EncToShareProtocol{}, err
|
|
}
|
|
|
|
e2s.params = params
|
|
e2s.zero = rlwe.NewSecretKey(params.Parameters)
|
|
e2s.maskBigint = make([]*big.Int, params.N())
|
|
for i := range e2s.maskBigint {
|
|
e2s.maskBigint[i] = new(big.Int)
|
|
}
|
|
e2s.buff = e2s.params.RingQ().NewPoly()
|
|
return e2s, nil
|
|
}
|
|
|
|
// AllocateShare allocates a share of the EncToShare protocol
|
|
func (e2s EncToShareProtocol) AllocateShare(level int) (share mhe.KeySwitchShare) {
|
|
return e2s.KeySwitchProtocol.AllocateShare(level)
|
|
}
|
|
|
|
// GenShare generates a party's share in the encryption-to-shares protocol. This share consist in the additive secret-share of the party
|
|
// which is written in secretShareOut and in the public masked-decryption share written in publicShareOut.
|
|
// This protocol requires additional inputs which are :
|
|
// logBound : the bit length of the masks
|
|
// ct: the ciphertext to share
|
|
// publicShareOut is always returned in the NTT domain.
|
|
// The method "GetMinimumLevelForBootstrapping" should be used to get the minimum level at which EncToShare can be called while still ensure 128-bits of security, as well as the
|
|
// value for logBound.
|
|
func (e2s EncToShareProtocol) GenShare(sk *rlwe.SecretKey, logBound uint, ct *rlwe.Ciphertext, secretShareOut *mhe.AdditiveShareBigint, publicShareOut *mhe.KeySwitchShare) (err error) {
|
|
|
|
levelQ := utils.Min(ct.Value[1].Level(), publicShareOut.Value.Level())
|
|
|
|
ringQ := e2s.params.RingQ().AtLevel(levelQ)
|
|
|
|
// Get the upperbound on the norm
|
|
// Ensures that bound >= 2^{128+logbound}
|
|
bound := bignum.NewInt(1)
|
|
bound.Lsh(bound, uint(logBound))
|
|
|
|
boundMax := new(big.Int).Set(ringQ.ModulusAtLevel[levelQ])
|
|
|
|
var sign int
|
|
|
|
sign = bound.Cmp(boundMax)
|
|
|
|
if sign == 1 || bound.Cmp(boundMax) == 1 {
|
|
return fmt.Errorf("cannot GenShare: ciphertext level is not large enough for refresh correctness")
|
|
}
|
|
|
|
boundHalf := new(big.Int).Rsh(bound, 1)
|
|
|
|
prng, _ := sampling.NewPRNG()
|
|
|
|
dslots := ct.Slots()
|
|
if ringQ.Type() == ring.Standard {
|
|
dslots *= 2
|
|
}
|
|
|
|
// Generate the mask in Z[Y] for Y = X^{N/(2*slots)}
|
|
for i := 0; i < dslots; i++ {
|
|
e2s.maskBigint[i] = bignum.RandInt(prng, bound)
|
|
sign = e2s.maskBigint[i].Cmp(boundHalf)
|
|
if sign == 1 || sign == 0 {
|
|
e2s.maskBigint[i].Sub(e2s.maskBigint[i], bound)
|
|
}
|
|
|
|
secretShareOut.Value[i].Set(e2s.maskBigint[i])
|
|
}
|
|
|
|
// Encrypt the mask
|
|
// Generates an encryption of zero and subtracts the mask
|
|
e2s.KeySwitchProtocol.GenShare(sk, e2s.zero, ct, publicShareOut)
|
|
|
|
// Positional -> RNS -> NTT
|
|
ringQ.SetCoefficientsBigint(secretShareOut.Value[:dslots], e2s.buff)
|
|
rlwe.NTTSparseAndMontgomery(ringQ, ct.MetaData, e2s.buff)
|
|
|
|
// Subtracts the mask to the encryption of zero
|
|
ringQ.Sub(publicShareOut.Value, e2s.buff, publicShareOut.Value)
|
|
|
|
return
|
|
}
|
|
|
|
// GetShare is the final step of the encryption-to-share protocol. It performs the masked decryption of the target ciphertext followed by a
|
|
// the removal of the caller's secretShare as generated in the GenShare method.
|
|
// If the caller is not secret-key-share holder (i.e., didn't generate a decryption share), `secretShare` can be set to nil.
|
|
// Therefore, in order to obtain an additive sharing of the message, only one party should call this method, and the other parties should use
|
|
// the secretShareOut output of the GenShare method.
|
|
func (e2s EncToShareProtocol) GetShare(secretShare *mhe.AdditiveShareBigint, aggregatePublicShare mhe.KeySwitchShare, ct *rlwe.Ciphertext, secretShareOut *mhe.AdditiveShareBigint) {
|
|
|
|
levelQ := utils.Min(ct.Level(), aggregatePublicShare.Value.Level())
|
|
|
|
ringQ := e2s.params.RingQ().AtLevel(levelQ)
|
|
|
|
// Adds the decryption share on the ciphertext and stores the result in a buff
|
|
ringQ.Add(aggregatePublicShare.Value, ct.Value[0], e2s.buff)
|
|
|
|
// INTT -> RNS -> Positional
|
|
ringQ.INTT(e2s.buff, e2s.buff)
|
|
|
|
dslots := ct.Slots()
|
|
if ringQ.Type() == ring.Standard {
|
|
dslots *= 2
|
|
}
|
|
|
|
gap := ringQ.N() / dslots
|
|
|
|
ringQ.PolyToBigintCentered(e2s.buff, gap, e2s.maskBigint)
|
|
|
|
// Subtracts the last mask
|
|
if secretShare != nil {
|
|
a := secretShareOut.Value
|
|
b := e2s.maskBigint
|
|
c := secretShare.Value
|
|
for i := range secretShareOut.Value[:dslots] {
|
|
a[i].Add(c[i], b[i])
|
|
}
|
|
} else {
|
|
a := secretShareOut.Value
|
|
b := e2s.maskBigint
|
|
for i := range secretShareOut.Value[:dslots] {
|
|
a[i].Set(b[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
// ShareToEncProtocol is the structure storing the parameters and temporary buffers
|
|
// required by the shares-to-encryption protocol.
|
|
type ShareToEncProtocol struct {
|
|
mhe.KeySwitchProtocol
|
|
params hefloat.Parameters
|
|
tmp ring.Poly
|
|
ssBigint []*big.Int
|
|
zero *rlwe.SecretKey
|
|
}
|
|
|
|
// ShallowCopy creates a shallow copy of ShareToEncProtocol in which all the read-only data-structures are
|
|
// shared with the receiver and the temporary buffers are reallocated. The receiver and the returned
|
|
// ShareToEncProtocol can be used concurrently.
|
|
func (s2e ShareToEncProtocol) ShallowCopy() ShareToEncProtocol {
|
|
return ShareToEncProtocol{
|
|
KeySwitchProtocol: s2e.KeySwitchProtocol.ShallowCopy(),
|
|
params: s2e.params,
|
|
tmp: s2e.params.RingQ().NewPoly(),
|
|
ssBigint: make([]*big.Int, s2e.params.N()),
|
|
zero: s2e.zero,
|
|
}
|
|
}
|
|
|
|
// NewShareToEncProtocol creates a new ShareToEncProtocol struct from the passed parameters.
|
|
func NewShareToEncProtocol(params hefloat.Parameters, noise ring.DistributionParameters) (ShareToEncProtocol, error) {
|
|
s2e := ShareToEncProtocol{}
|
|
|
|
var err error
|
|
if s2e.KeySwitchProtocol, err = mhe.NewKeySwitchProtocol(params.Parameters, noise); err != nil {
|
|
return ShareToEncProtocol{}, err
|
|
}
|
|
|
|
s2e.params = params
|
|
s2e.tmp = s2e.params.RingQ().NewPoly()
|
|
s2e.ssBigint = make([]*big.Int, s2e.params.N())
|
|
s2e.zero = rlwe.NewSecretKey(params.Parameters)
|
|
return s2e, nil
|
|
}
|
|
|
|
// AllocateShare allocates a share of the ShareToEnc protocol
|
|
func (s2e ShareToEncProtocol) AllocateShare(level int) (share mhe.KeySwitchShare) {
|
|
return s2e.KeySwitchProtocol.AllocateShare(level)
|
|
}
|
|
|
|
// GenShare generates a party's in the shares-to-encryption protocol given the party's secret-key share `sk`, a common
|
|
// polynomial sampled from the CRS `crs` and the party's secret share of the message.
|
|
func (s2e ShareToEncProtocol) GenShare(sk *rlwe.SecretKey, crs mhe.KeySwitchCRP, metadata *rlwe.MetaData, secretShare mhe.AdditiveShareBigint, c0ShareOut *mhe.KeySwitchShare) (err error) {
|
|
|
|
if crs.Value.Level() != c0ShareOut.Value.Level() {
|
|
return fmt.Errorf("cannot GenShare: crs and c0ShareOut level must be equal")
|
|
}
|
|
|
|
ringQ := s2e.params.RingQ().AtLevel(crs.Value.Level())
|
|
|
|
// Generates an encryption share
|
|
ct := &rlwe.Ciphertext{}
|
|
ct.Value = []ring.Poly{{}, crs.Value}
|
|
ct.MetaData = &rlwe.MetaData{}
|
|
ct.IsNTT = true
|
|
s2e.KeySwitchProtocol.GenShare(s2e.zero, sk, ct, c0ShareOut)
|
|
|
|
dslots := metadata.Slots()
|
|
if ringQ.Type() == ring.Standard {
|
|
dslots *= 2
|
|
}
|
|
|
|
// Positional -> RNS -> NTT
|
|
ringQ.SetCoefficientsBigint(secretShare.Value[:dslots], s2e.tmp)
|
|
|
|
rlwe.NTTSparseAndMontgomery(ringQ, metadata, s2e.tmp)
|
|
|
|
ringQ.Add(c0ShareOut.Value, s2e.tmp, c0ShareOut.Value)
|
|
|
|
return
|
|
}
|
|
|
|
// GetEncryption computes the final encryption of the secret-shared message when provided with the aggregation `c0Agg` of the parties'
|
|
// share in the protocol and with the common, CRS-sampled polynomial `crs`.
|
|
func (s2e ShareToEncProtocol) GetEncryption(c0Agg mhe.KeySwitchShare, crs mhe.KeySwitchCRP, opOut *rlwe.Ciphertext) (err error) {
|
|
|
|
if opOut.Degree() != 1 {
|
|
return fmt.Errorf("cannot GetEncryption: opOut must have degree 1")
|
|
}
|
|
|
|
if c0Agg.Value.Level() != crs.Value.Level() {
|
|
return fmt.Errorf("cannot GetEncryption: c0Agg level must be equal to crs level")
|
|
}
|
|
|
|
if opOut.Level() != crs.Value.Level() {
|
|
return fmt.Errorf("cannot GetEncryption: opOut level must be equal to crs level")
|
|
}
|
|
|
|
opOut.Value[0].Copy(c0Agg.Value)
|
|
opOut.Value[1].Copy(crs.Value)
|
|
|
|
return
|
|
}
|