Decryptor::invariant_noise_budget does not always return 0 when the error has entered the message space.
Since, at least for BFV, the function calculates the noise by canceling the message term with
$t(\frac{q}{t}m+e)\mathrm{\ }mod\mathrm{\ }q$
$=qm+e \mathrm{\ }mod\mathrm{\ } q$
$=e\mathrm{\ }mod\mathrm{\ } q$
By noise-growing techniques like adding a ciphertext by itself repeatedly, one could get $e' \geq \frac{q}{t}e$, where by substituting $e$ with $e'$:
$t(\frac{q}{t}m+\frac{q}{t}e)\mathrm{\ }mod\mathrm{\ }q$
$=qm+qe\mathrm{\ }mod\mathrm{\ }q$
$= 0$
Since the question of whether the noise has entered the message space is both an integrity and a secrecy issue, the docstring of the function should clearly state that it provides no correctness guarantee and should only be used for reference/sanity check, not for security purposes.
This example in C# shows that the function sometimes returns a non-zero value when the decryption is not correct:
static void Main(string[] args)
{
using EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV);
parms.PolyModulusDegree = 8192;
parms.CoeffModulus = CoeffModulus.BFVDefault(parms.PolyModulusDegree);
parms.PlainModulus = PlainModulus.Batching(parms.PolyModulusDegree, 20);
using SEALContext context = new SEALContext(parms);
using KeyGenerator keygen = new KeyGenerator(context);
using SecretKey secretKey = keygen.SecretKey;
keygen.CreatePublicKey(out PublicKey publicKey);
using BatchEncoder batchEncoder = new BatchEncoder(context);
using Encryptor encryptor = new Encryptor(context, publicKey);
using Evaluator evaluator = new Evaluator(context);
using Decryptor decryptor = new Decryptor(context, secretKey);
using Plaintext plaintextZero = new Plaintext();
batchEncoder.Encode(new ulong[batchEncoder.SlotCount], plaintextZero);
Ciphertext ciphertext = new Ciphertext();
encryptor.Encrypt(plaintextZero, ciphertext);
for (int i = 0; ; i++)
{
evaluator.AddInplace(ciphertext, ciphertext);
using Plaintext plaintextAddResult = new Plaintext();
decryptor.Decrypt(ciphertext, plaintextAddResult);
int budget = decryptor.InvariantNoiseBudget(ciphertext);
string plaintextAddResultStrIsZero = plaintextAddResult[0] == 0 ? "is zero" : "is not zero";
Console.WriteLine($"[{i}]: Budget is {budget}, plaintext {plaintextAddResultStrIsZero}");
if (budget != 0 && plaintextAddResult[0] != 0)
{ Console.WriteLine($"Noise budget is not zero at {i} when the decryption is not correct"); return; }
}
}
Decryptor::invariant_noise_budget does not always return 0 when the error has entered the message space.
Since, at least for BFV, the function calculates the noise by canceling the message term with
$t(\frac{q}{t}m+e)\mathrm{\ }mod\mathrm{\ }q$
$=qm+e \mathrm{\ }mod\mathrm{\ } q$
$=e\mathrm{\ }mod\mathrm{\ } q$
By noise-growing techniques like adding a ciphertext by itself repeatedly, one could get$e' \geq \frac{q}{t}e$ , where by substituting $e$ with $e'$ :
Since the question of whether the noise has entered the message space is both an integrity and a secrecy issue, the docstring of the function should clearly state that it provides no correctness guarantee and should only be used for reference/sanity check, not for security purposes.
This example in C# shows that the function sometimes returns a non-zero value when the decryption is not correct: