lattigo icon indicating copy to clipboard operation
lattigo copied to clipboard

Bug[BGV]: panic when encoding `N` values with `IsBatched=false`

Open Pro7ech opened this issue 1 year ago • 2 comments

What version of Lattigo are you using?

v5.0.2

Does this issue persist with the latest release?

yes

What were you trying to do?

Encode params.N() values with the flag IsBatched=false

What were you expecting to happen?

No error

What actually happened?

panic: cannot Encode (TimeDomain): len(values)=16384 > N=128

Reproducibility

// Package main is a template encrypted modular arithmetic integers, with a set of example parameters, key generation, encoding, encryption, decryption and decoding.
package main

import (
	"fmt"
	"math/rand"

	"github.com/tuneinsight/lattigo/v5/core/rlwe"
	"github.com/tuneinsight/lattigo/v5/he/heint"
	"github.com/tuneinsight/lattigo/v5/utils"
)

func main() {
	var err error
	var params heint.Parameters

	// 128-bit secure parameters enabling depth-7 circuits.
	// LogN:14, LogQP: 431.
	if params, err = heint.NewParametersFromLiteral(
		heint.ParametersLiteral{
			LogN:             14,                                    // log2(ring degree)
			LogQ:             []int{55, 45, 45, 45, 45, 45, 45, 45}, // log2(primes Q) (ciphertext modulus)
			LogP:             []int{61},                             // log2(primes P) (auxiliary modulus)
			PlaintextModulus: 0x101,                               // log2(scale)
		}); err != nil {
		panic(err)
	}

	// Key Generator
	kgen := rlwe.NewKeyGenerator(params)

	// Secret Key
	sk := kgen.GenSecretKeyNew()

	// Encoder
	ecd := heint.NewEncoder(params)

	// Encryptor
	enc := rlwe.NewEncryptor(params, sk)

	// Decryptor
	dec := rlwe.NewDecryptor(params, sk)

	// Vector of plaintext values
	values := make([]uint64, params.N())

	// Source for sampling random plaintext values (not cryptographically secure)
	/* #nosec G404 */
	r := rand.New(rand.NewSource(0))

	// Populates the vector of plaintext values
	T := params.PlaintextModulus()
	for i := range values {
		values[i] = r.Uint64() % T
	}

	// Allocates a plaintext at the max level.
	// Default rlwe.MetaData:
	// - IsBatched = true (slots encoding)
	// - Scale = params.DefaultScale()
	pt := heint.NewPlaintext(params, params.MaxLevel())
	pt.IsBatched = false

	// Encodes the vector of plaintext values
	if err = ecd.Encode(values, pt); err != nil {
		panic(err)
	}

	// Encrypts the vector of plaintext values
	var ct *rlwe.Ciphertext
	if ct, err = enc.EncryptNew(pt); err != nil {
		panic(err)
	}

	// Allocates a vector for the reference values
	want := make([]uint64, params.MaxSlots())
	copy(want, values)

	PrintPrecisionStats(params, ct, want, ecd, dec)
}

// PrintPrecisionStats decrypts, decodes and prints the precision stats of a ciphertext.
func PrintPrecisionStats(params heint.Parameters, ct *rlwe.Ciphertext, want []uint64, ecd *heint.Encoder, dec *rlwe.Decryptor) {

	var err error

	// Decrypts the vector of plaintext values
	pt := dec.DecryptNew(ct)

	// Decodes the plaintext
	have := make([]uint64, params.MaxSlots())
	if err = ecd.Decode(pt, have); err != nil {
		panic(err)
	}

	// Pretty prints some values
	fmt.Printf("Have: ")
	for i := 0; i < 4; i++ {
		fmt.Printf("%d ", have[i])
	}
	fmt.Printf("...\n")

	fmt.Printf("Want: ")
	for i := 0; i < 4; i++ {
		fmt.Printf("%d ", want[i])
	}
	fmt.Printf("...\n")

	if !utils.EqualSlice(want, have) {
		panic("wrong result: bad decryption or encrypted/plaintext circuits do not match")
	}
}

Pro7ech avatar May 28 '24 07:05 Pro7ech

Hey, this seems to be an API issue as it is not documented what happens if the plaintext modulus t does not respect t = 1 (mod 2N).

Internally, the current implementation will find a ring degree for the T polynomial that abides by this rule. In the snippet t = 257, the largest ring degree that accommodates this choice is 128 which is also the number of slots available. As a result, params.N() does not correspond to the actual number of slots available.

For example see, testEncoder in schemes/bgv/bgv_test.go which handles this case correctly.

qantik avatar Jun 04 '24 14:06 qantik

If batching is set to false, it should be possible to encode N values modulo a any t since this doesn't require to call a DFT/IDFT.

Pro7ech avatar Jun 07 '24 06:06 Pro7ech