-
Notifications
You must be signed in to change notification settings - Fork 180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug[hefloat/bootstrapping]: bootstrapping.NewEvaluator
modifies bootstrapping.Parameters
#460
Labels
bug
Something isn't working
Comments
Hey, this seems to be quite a serious issue. However I'm not able to reproduce it on my end, i.e., triggering a change in Can you give an minimal working example, that showcases this bug? |
Yes my bad, I should have added a code snippet. Here is the bootstrapping example that triggers the bug // Package main implements an example showcasing the basics of the bootstrapping for fixed-point approximate arithmetic over the reals/complexes.
// The bootstrapping is a circuit that homomorphically re-encrypts a ciphertext at level zero to a ciphertext at a higher level, enabling further computations.
// Note that, unlike other bootstrappings (BGV/BFV/TFHE), the this bootstrapping does not reduce the error in the ciphertext, but only enables further computations.
// This example shows how to bootstrap a single ciphertext whose ring degree is the same as the one of the bootstrapping parameters.
// Use the flag -short to run the examples fast but with insecure parameters.
package main
import (
"flag"
"fmt"
"math"
"math/big"
"github.com/tuneinsight/lattigo/v5/core/rlwe"
"github.com/tuneinsight/lattigo/v5/he/hefloat"
"github.com/tuneinsight/lattigo/v5/he/hefloat/bootstrapping"
"github.com/tuneinsight/lattigo/v5/ring"
"github.com/tuneinsight/lattigo/v5/utils"
"github.com/tuneinsight/lattigo/v5/utils/sampling"
)
var flagShort = flag.Bool("short", false, "run the example with a smaller and insecure ring degree.")
func main() {
flag.Parse()
// Default LogN, which with the following defined parameters
// provides a security of 128-bit.
LogN := 16
if *flagShort {
LogN -= 3
}
//==============================
//=== 1) RESIDUAL PARAMETERS ===
//==============================
// First we must define the residual parameters.
// The residual parameters are the parameters used outside of the bootstrapping circuit.
// For this example, we have a LogN=16, logQ = 55 + 10*40 and logP = 3*61, so LogQP = 638.
// With LogN=16, LogQP=638 and H=192, these parameters achieve well over 128-bit of security.
params, err := hefloat.NewParametersFromLiteral(hefloat.ParametersLiteral{
LogN: LogN, // Log2 of the ring degree
LogQ: []int{55, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40}, // Log2 of the ciphertext prime moduli
LogP: []int{61, 61, 61}, // Log2 of the key-switch auxiliary prime moduli
LogDefaultScale: 40, // Log2 of the scale
Xs: ring.Ternary{H: 192},
})
if err != nil {
panic(err)
}
//==========================================
//=== 2) BOOTSTRAPPING PARAMETERSLITERAL ===
//==========================================
// The bootstrapping circuit use its own Parameters which will be automatically
// instantiated given the residual parameters and the bootstrapping parameters.
// !WARNING! The bootstrapping parameters are not ensure to be 128-bit secure, it is the
// responsibility of the user to check that the meet the security requirement and tweak them if necessary.
// Note that the default bootstrapping parameters use LogN=16 and a ternary secret with H=192 non-zero coefficients
// which provides parameters which are at least 128-bit if their LogQP <= 1550.
// For this first example, we do not specify any circuit specific optional field in the bootstrapping parameters literal.
// Thus we expect the bootstrapping to give a precision of 27.25 bits with H=192 (and 23.8 with H=N/2)
// if the plaintext values are uniformly distributed in [-1, 1] for both the real and imaginary part.
// See `he/float/bootstrapping/parameters_literal.go` for detailed information about the optional fields.
btpParametersLit := bootstrapping.ParametersLiteral{
// We specify LogN to ensure that both the residual parameters and the bootstrapping parameters
// have the same LogN. This is not required, but we want it for this example.
LogN: utils.Pointy(LogN),
// In this example we need manually specify the number of auxiliary primes (i.e. #Pi) used by the
// evaluation keys of the bootstrapping circuit, so that the size of LogQP meets the security target.
LogP: []int{61, 61, 61, 61},
// In this example we manually specify the bootstrapping parameters' secret distribution.
// This is not necessary, but we ensure here that they are the same as the residual parameters.
Xs: params.Xs(),
}
//===================================
//=== 3) BOOTSTRAPPING PARAMETERS ===
//===================================
// Now that the residual parameters and the bootstrapping parameters literals are defined, we can instantiate
// the bootstrapping parameters.
// The instantiated bootstrapping parameters store their own hefloat.Parameter, which are the parameters of the
// ring used by the bootstrapping circuit.
// The bootstrapping parameters are a wrapper of hefloat.Parameters, with additional information.
// They therefore has the same API as the hefloat.Parameters and we can use this API to print some information.
btpParams, err := bootstrapping.NewParametersFromLiteral(params, btpParametersLit)
if err != nil {
panic(err)
}
if *flagShort {
// Corrects the message ratio Q0/|m(X)| to take into account the smaller number of slots and keep the same precision
btpParams.Mod1ParametersLiteral.LogMessageRatio += 16 - params.LogN()
}
// We print some information about the residual parameters.
fmt.Printf("Residual parameters: logN=%d, logSlots=%d, H=%d, sigma=%f, logQP=%f, levels=%d, scale=2^%d\n",
btpParams.ResidualParameters.LogN(),
btpParams.ResidualParameters.LogMaxSlots(),
btpParams.ResidualParameters.XsHammingWeight(),
btpParams.ResidualParameters.Xe(), params.LogQP(),
btpParams.ResidualParameters.MaxLevel(),
btpParams.ResidualParameters.LogDefaultScale())
// And some information about the bootstrapping parameters.
// We can notably check that the LogQP of the bootstrapping parameters is smaller than 1550, which ensures
// 128-bit of security as explained above.
fmt.Printf("Bootstrapping parameters: logN=%d, logSlots=%d, H(%d; %d), sigma=%f, logQP=%f, levels=%d, scale=2^%d\n",
btpParams.BootstrappingParameters.LogN(),
btpParams.BootstrappingParameters.LogMaxSlots(),
btpParams.BootstrappingParameters.XsHammingWeight(),
btpParams.EphemeralSecretWeight,
btpParams.BootstrappingParameters.Xe(),
btpParams.BootstrappingParameters.LogQP(),
btpParams.BootstrappingParameters.QCount(),
btpParams.BootstrappingParameters.LogDefaultScale())
//===========================
//=== 4) KEYGEN & ENCRYPT ===
//===========================
// Now that both the residual and bootstrapping parameters are instantiated, we can
// instantiate the usual necessary object to encode, encrypt and decrypt.
// Scheme context and keys
kgen := rlwe.NewKeyGenerator(params)
sk, pk := kgen.GenKeyPairNew()
encoder := hefloat.NewEncoder(params)
decryptor := rlwe.NewDecryptor(params, sk)
encryptor := rlwe.NewEncryptor(params, pk)
fmt.Println()
fmt.Println("Generating bootstrapping evaluation keys...")
evk, _, err := btpParams.GenEvaluationKeys(sk)
if err != nil {
panic(err)
}
fmt.Println("Done")
//========================
//=== 5) BOOTSTRAPPING ===
//========================
btpParams.CoeffsToSlotsParameters.Scaling = new(big.Float).SetFloat64(1)
// Instantiates the bootstrapper
var eval *bootstrapping.Evaluator
if eval, err = bootstrapping.NewEvaluator(btpParams, evk); err != nil {
panic(err)
}
fmt.Println(btpParams.CoeffsToSlotsParameters.Scaling)
if eval, err = bootstrapping.NewEvaluator(btpParams, evk); err != nil {
panic(err)
}
fmt.Println(btpParams.CoeffsToSlotsParameters.Scaling)
// Generate a random plaintext with values uniformely distributed in [-1, 1] for the real and imaginary part.
valuesWant := make([]complex128, params.MaxSlots())
for i := range valuesWant {
valuesWant[i] = sampling.RandComplex128(-1, 1)
}
// We encrypt at level 0
plaintext := hefloat.NewPlaintext(params, 0)
if err := encoder.Encode(valuesWant, plaintext); err != nil {
panic(err)
}
// Encrypt
ciphertext1, err := encryptor.EncryptNew(plaintext)
if err != nil {
panic(err)
}
// Decrypt, print and compare with the plaintext values
fmt.Println()
fmt.Println("Precision of values vs. ciphertext")
valuesTest1 := printDebug(params, ciphertext1, valuesWant, decryptor, encoder)
// Bootstrap the ciphertext (homomorphic re-encryption)
// It takes a ciphertext at level 0 (if not at level 0, then it will reduce it to level 0)
// and returns a ciphertext with the max level of `floatParamsResidualLit`.
// CAUTION: the scale of the ciphertext MUST be equal (or very close) to params.DefaultScale()
// To equalize the scale, the function evaluator.SetScale(ciphertext, parameters.DefaultScale()) can be used at the expense of one level.
// If the ciphertext is is at level one or greater when given to the bootstrapper, this equalization is automatically done.
fmt.Println("Bootstrapping...")
ciphertext2, err := eval.Bootstrap(ciphertext1)
if err != nil {
panic(err)
}
fmt.Println("Done")
//==================
//=== 6) DECRYPT ===
//==================
// Decrypt, print and compare with the plaintext values
fmt.Println()
fmt.Println("Precision of ciphertext vs. Bootstrap(ciphertext)")
printDebug(params, ciphertext2, valuesTest1, decryptor, encoder)
}
func printDebug(params hefloat.Parameters, ciphertext *rlwe.Ciphertext, valuesWant []complex128, decryptor *rlwe.Decryptor, encoder *hefloat.Encoder) (valuesTest []complex128) {
valuesTest = make([]complex128, ciphertext.Slots())
if err := encoder.Decode(decryptor.DecryptNew(ciphertext), valuesTest); err != nil {
panic(err)
}
fmt.Println()
fmt.Printf("Level: %d (logQ = %d)\n", ciphertext.Level(), params.LogQLvl(ciphertext.Level()))
fmt.Printf("Scale: 2^%f\n", math.Log2(ciphertext.Scale.Float64()))
fmt.Printf("ValuesTest: %6.10f %6.10f %6.10f %6.10f...\n", valuesTest[0], valuesTest[1], valuesTest[2], valuesTest[3])
fmt.Printf("ValuesWant: %6.10f %6.10f %6.10f %6.10f...\n", valuesWant[0], valuesWant[1], valuesWant[2], valuesWant[3])
precStats := hefloat.GetPrecisionStats(params, encoder, nil, valuesWant, valuesTest, 0, false)
fmt.Println(precStats.String())
fmt.Println()
return
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Instantiating a
bootstrapping.Evaluator
modifies thebootstrapping.Parameters
template that is given as input. Reusing the samebootstrapping.Parameters
to instantiate a secondbootstrapping.Evaluator
produces abootstrapping.Evaluator
that has incorrect internal initialization of the plaintext constants.This bug is caused by
bootstrapping.Parameters
having some field that are pointer to valuesbootstrapping.Parameters
being copied in thebootstrapping.Evaluator
without allocating new pointers for the said valuesbootstrapping.Evaluator
The text was updated successfully, but these errors were encountered: