Skip to content

Commit

Permalink
collapse bfv
Browse files Browse the repository at this point in the history
  • Loading branch information
qantik committed Aug 4, 2024
1 parent e2e3721 commit 1487afa
Show file tree
Hide file tree
Showing 16 changed files with 300 additions and 1,202 deletions.
2 changes: 1 addition & 1 deletion circuits/bgv/lintrans/lintrans_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestLinearTransformation(t *testing.T) {
for _, plaintextModulus := range testPlaintextModulus[:] {
p.PlaintextModulus = plaintextModulus

tc := bgv.NewTestContext(p)
tc := bgv.NewTestContext(p, false)

for _, testSet := range []func(tc *bgv.TestContext, t *testing.T){
run,
Expand Down
32 changes: 4 additions & 28 deletions circuits/bgv/polynomial/polynomial_evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (

"github.com/tuneinsight/lattigo/v5/circuits/common/polynomial"
"github.com/tuneinsight/lattigo/v5/core/rlwe"
"github.com/tuneinsight/lattigo/v5/schemes"
"github.com/tuneinsight/lattigo/v5/schemes/bfv"
"github.com/tuneinsight/lattigo/v5/schemes/bgv"
)

Expand All @@ -16,38 +14,16 @@ type Evaluator struct {
InvariantTensoring bool
}

// NewEvaluator instantiates a new PolynomialEvaluator from a [schemes.Evaluator].
// The default [bgv.Evaluator] is compliant to the [schemes.Evaluator] interface.
// InvariantTensoring is a boolean that specifies if the evaluator performed the invariant tensoring (BFV-style) or
// the regular tensoring (BGV-style).
func NewEvaluator(params bgv.Parameters, eval schemes.Evaluator, InvariantTensoring bool) *Evaluator {

var evalForPoly schemes.Evaluator

switch eval := eval.(type) {
case *bgv.Evaluator:
if InvariantTensoring {
evalForPoly = scaleInvariantEvaluator{eval}
} else {
evalForPoly = eval
}
case *bfv.Evaluator:
if InvariantTensoring {
evalForPoly = eval
} else {
evalForPoly = eval.Evaluator
}
default:
evalForPoly = eval
}
// NewEvaluator instantiates a new PolynomialEvaluator from a [bgv.Evaluator].
func NewEvaluator(params bgv.Parameters, eval *bgv.Evaluator) *Evaluator {

return &Evaluator{
Parameters: params,
Evaluator: polynomial.Evaluator[uint64]{
Evaluator: evalForPoly,
Evaluator: eval,
CoefficientGetter: CoefficientGetter{values: make([]uint64, params.MaxSlots())},
},
InvariantTensoring: InvariantTensoring,
InvariantTensoring: eval.ScaleInvariant,
}
}

Expand Down
7 changes: 4 additions & 3 deletions circuits/bgv/polynomial/polynomial_evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestPolynomialEvaluator(t *testing.T) {
for _, plaintextModulus := range testPlaintextModulus[:] {
p.PlaintextModulus = plaintextModulus

tc := bgv.NewTestContext(p)
tc := bgv.NewTestContext(p, false)

for _, testSet := range []func(tc *bgv.TestContext, t *testing.T){
run,
Expand Down Expand Up @@ -65,7 +65,7 @@ func run(tc *bgv.TestContext, t *testing.T) {

t.Run("Standard"+tc.String(), func(t *testing.T) {

polyEval := NewEvaluator(tc.Params, tc.Evl, false)
polyEval := NewEvaluator(tc.Params, tc.Evl)

res, err := polyEval.Evaluate(ciphertext, poly, tc.Params.DefaultScale())
require.NoError(t, err)
Expand All @@ -76,8 +76,9 @@ func run(tc *bgv.TestContext, t *testing.T) {
})

t.Run("Invariant"+tc.String(), func(t *testing.T) {
tc.Evl.ScaleInvariant = true

polyEval := NewEvaluator(tc.Params, tc.Evl, true)
polyEval := NewEvaluator(tc.Params, tc.Evl)

res, err := polyEval.Evaluate(ciphertext, poly, tc.Params.DefaultScale())
require.NoError(t, err)
Expand Down
3 changes: 1 addition & 2 deletions circuits/ckks/polynomial/polynomial_evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

"github.com/tuneinsight/lattigo/v5/circuits/common/polynomial"
"github.com/tuneinsight/lattigo/v5/core/rlwe"
"github.com/tuneinsight/lattigo/v5/schemes"
"github.com/tuneinsight/lattigo/v5/schemes/ckks"
"github.com/tuneinsight/lattigo/v5/utils/bignum"
)
Expand All @@ -19,7 +18,7 @@ type Evaluator struct {

// NewEvaluator instantiates a new [Evaluator] from a [ckks.Evaluator].
// This method is allocation free.
func NewEvaluator(params ckks.Parameters, eval schemes.Evaluator) *Evaluator {
func NewEvaluator(params ckks.Parameters, eval *ckks.Evaluator) *Evaluator {
return &Evaluator{
Parameters: params,
Evaluator: polynomial.Evaluator[*bignum.Complex]{
Expand Down
3 changes: 2 additions & 1 deletion lattigo-hierarchy.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion schemes/bfv/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The BFV package provides an RNS-accelerated implementation of the Fan-Vercautere

## Implementation Notes

The proposed implementation is built as a wrapper over the `bgv` package, which implements a unified variant of the BFV and BGV schemes. The only practical difference with the standard BFV is that the plaintext modulus must be coprime with the ciphertext modulus. This is both required for correctness ($T^{-1}\mod Q$ must be defined) and for security reasons (if $T|Q$ then the BGV scheme is not IND-CPA secure anymore).
The proposed implementation is built as a wrapper over the `bgv` package, which implements a unified variant of the BFV and BGV schemes. The only practical difference with the standard BFV is that the plaintext modulus must be coprime with the ciphertext modulus. This is both required for correctness ($T^{-1}\mod Q$ must be defined) and for security reasons (if $T|Q$ then the BGV scheme is not IND-CPA secure anymore). To instantiate the BFV cryptosystem, generate a new BGV evaluator by with the optional scale-invariant parameter set to `true`.

For additional information, see the [`README.md`](../bgv/README.md) in the `bgv` package.

Expand Down
180 changes: 2 additions & 178 deletions schemes/bfv/bfv.go
Original file line number Diff line number Diff line change
@@ -1,180 +1,4 @@
// Package bfv provides an RNS-accelerated implementation of the Fan-Vercauteren version of Brakerski's (BFV) scale-invariant homomorphic encryption scheme.
// The BFV scheme enables SIMD modular arithmetic over encrypted vectors or integers.
// The BFV scheme enables SIMD modular arithmetic over encrypted vectors or integers. See `README.md` for more information
// on how to instantiate a BFV evaluator.
package bfv

import (
"fmt"

"github.com/tuneinsight/lattigo/v5/core/rlwe"
"github.com/tuneinsight/lattigo/v5/ring"
"github.com/tuneinsight/lattigo/v5/schemes/bgv"
)

// NewPlaintext allocates a new [rlwe.Plaintext] from the BFV parameters, at the
// specified level. If the level argument is not provided, the plaintext is
// initialized at level params.MaxLevelQ().
//
// The plaintext is initialized with its metadata so that it can be passed to a,
// [bfv.Encoder]. Before doing so, the user can update the MetaData field to set
// a specific scaling factor,
// plaintext dimensions (if applicable) or encoding domain.
func NewPlaintext(params Parameters, level ...int) (pt *rlwe.Plaintext) {
pt = rlwe.NewPlaintext(params, level...)
pt.IsBatched = true
pt.Scale = params.DefaultScale()
pt.LogDimensions = params.LogMaxDimensions()
return
}

// NewCiphertext allocates a new [rlwe.Ciphertext] from the BFV parameters,
// at the specified level and ciphertext degree. If the level argument is not
// provided, the ciphertext is initialized at level params.MaxLevelQ().
//
// To create a ciphertext for encrypting a new message, the ciphertext should be
// at degree 1.
func NewCiphertext(params Parameters, degree int, level ...int) (ct *rlwe.Ciphertext) {
ct = rlwe.NewCiphertext(params, degree, level...)
ct.IsBatched = true
ct.Scale = params.DefaultScale()
ct.LogDimensions = params.LogMaxDimensions()
return
}

// NewEncryptor instantiates a new [rlwe.Encryptor] from the given BFV parameters and
// encryption key. This key can be either a *[rlwe.SecretKey] or a *[rlwe.PublicKey].
func NewEncryptor(params Parameters, key rlwe.EncryptionKey) *rlwe.Encryptor {
return rlwe.NewEncryptor(params, key)
}

// NewDecryptor instantiates a new [rlwe.Decryptor] from the given BFV parameters and
// secret decryption key.
func NewDecryptor(params Parameters, key *rlwe.SecretKey) *rlwe.Decryptor {
return rlwe.NewDecryptor(params, key)
}

// NewKeyGenerator instantiates a new [rlwe.KeyGenerator] from the given
// BFV parameters.
func NewKeyGenerator(params Parameters) *rlwe.KeyGenerator {
return rlwe.NewKeyGenerator(params)
}

// Encoder is a structure that stores the parameters to encode values on a plaintext in a SIMD (Single-Instruction Multiple-Data) fashion.
type Encoder struct {
*bgv.Encoder
}

// NewEncoder creates a new [Encoder] from the provided parameters.
func NewEncoder(params Parameters) *Encoder {
return &Encoder{bgv.NewEncoder(params.Parameters)}
}

// ShallowCopy creates a shallow copy of this [Encoder] in which the read-only data-structures are
// shared with the receiver.
func (e Encoder) ShallowCopy() *Encoder {
return &Encoder{Encoder: e.Encoder.ShallowCopy()}
}

// Evaluator is a struct that holds the necessary elements to perform the homomorphic operations between ciphertexts and/or plaintexts.
// It also holds a memory buffer used to store intermediate computations.
type Evaluator struct {
*bgv.Evaluator
}

// NewEvaluator creates a new [Evaluator], that can be used to do homomorphic
// operations on ciphertexts and/or plaintexts. It stores a memory buffer
// and ciphertexts that will be used for intermediate values.
func NewEvaluator(params Parameters, evk rlwe.EvaluationKeySet) *Evaluator {
return &Evaluator{bgv.NewEvaluator(params.Parameters, evk)}
}

// WithKey creates a shallow copy of this [Evaluator] in which the read-only data-structures are
// shared with the receiver but for which the evaluation key is set to the provided [rlwe.EvaluationKeySet].
func (eval Evaluator) WithKey(evk rlwe.EvaluationKeySet) *Evaluator {
return &Evaluator{eval.Evaluator.WithKey(evk)}
}

// ShallowCopy creates a shallow copy of this [Evaluator] in which the read-only data-structures are
// shared with the receiver.
func (eval Evaluator) ShallowCopy() *Evaluator {
return &Evaluator{eval.Evaluator.ShallowCopy()}
}

// Mul multiplies op0 with op1 without relinearization and returns the result in opOut.
// inputs:
// - op0: an *[rlwe.Ciphertext]
// - op1:
// - [rlwe.ElementInterface][ring.Poly]
// - *big.Int, uint64, int64, int
// - []uint64 or []int64 (of size at most N where N is the smallest integer satisfying PlaintextModulus = 1 mod 2N)
// - opOut: an *[rlwe.Ciphertext]
//
// The procedure will return an error if either op0 or op1 are have a degree higher than 1.
// The procedure will return an error if opOut.Degree != op0.Degree + op1.Degree.
func (eval Evaluator) Mul(op0 *rlwe.Ciphertext, op1 rlwe.Operand, opOut *rlwe.Ciphertext) (err error) {
switch op1 := op1.(type) {
case rlwe.ElementInterface[ring.Poly], []uint64:
return eval.Evaluator.MulScaleInvariant(op0, op1, opOut)
case uint64, int64, int:
return eval.Evaluator.Mul(op0, op1, op0)
default:
return fmt.Errorf("invalid op1.(Type), expected rlwe.ElementInterface[ring.Poly], []uint64 or uint64, int64, int, but got %T", op1)
}

}

// MulNew multiplies op0 with op1 without relinearization and returns the result in a new opOut.
// inputs:
// - op0: an *[rlwe.Ciphertext]
// - op1:
// - [rlwe.ElementInterface][[ring.Poly]]
// - *big.Int, uint64, int64, int
// - []uint64 or []int64 (of size at most N where N is the smallest integer satisfying PlaintextModulus = 1 mod 2N)
// - opOut: an *[rlwe.Ciphertext]
//
// The procedure will return an error if either op0.Degree or op1.Degree > 1.
func (eval Evaluator) MulNew(op0 *rlwe.Ciphertext, op1 rlwe.Operand) (opOut *rlwe.Ciphertext, err error) {
switch op1 := op1.(type) {
case rlwe.ElementInterface[ring.Poly], []uint64:
return eval.Evaluator.MulScaleInvariantNew(op0, op1)
case uint64, int64, int:
return eval.Evaluator.MulNew(op0, op1)
default:
return nil, fmt.Errorf("invalid op1.(Type), expected rlwe.ElementInterface[ring.Poly], []uint64 or uint64, int64, int, but got %T", op1)
}
}

// MulRelinNew multiplies op0 with op1 with relinearization and returns the result in a new opOut.
// inputs:
// - op0: an *[rlwe.Ciphertext]
// - op1:
// - [rlwe.ElementInterface][[ring.Poly]]
// - *big.Int, uint64, int64, int
// - []uint64 or []int64 (of size at most N where N is the smallest integer satisfying PlaintextModulus = 1 mod 2N)
// - opOut: an *[rlwe.Ciphertext]
//
// The procedure will return an error if either op0.Degree or op1.Degree > 1.
// The procedure will return an error if the evaluator was not created with an relinearization key.
func (eval Evaluator) MulRelinNew(op0 *rlwe.Ciphertext, op1 rlwe.Operand) (opOut *rlwe.Ciphertext, err error) {
return eval.Evaluator.MulRelinScaleInvariantNew(op0, op1)
}

// MulRelin multiplies op0 with op1 with relinearization and returns the result in opOut.
// inputs:
// - op0: an *[rlwe.Ciphertext]
// - op1:
// - [rlwe.ElementInterface][[ring.Poly]]
// - *big.Int, uint64, int64, int
// - []uint64 or []int64 (of size at most N where N is the smallest integer satisfying PlaintextModulus = 1 mod 2N)
// - opOut: an *[rlwe.Ciphertext]
//
// The procedure will return an error if either op0.Degree or op1.Degree > 1.
// The procedure will return an error if opOut.Degree != op0.Degree + op1.Degree.
// The procedure will return an error if the evaluator was not created with an relinearization key.
func (eval Evaluator) MulRelin(op0 *rlwe.Ciphertext, op1 rlwe.Operand, opOut *rlwe.Ciphertext) (err error) {
return eval.Evaluator.MulRelinScaleInvariant(op0, op1, opOut)
}

// Rescale does nothing when instantiated with the BFV scheme.
func (eval Evaluator) Rescale(op0, op1 *rlwe.Ciphertext) (err error) {
return nil
}
Loading

0 comments on commit 1487afa

Please sign in to comment.