Ejemplo n.º 1
0
        public static SEALContext GetContext()
        {
            var encryptionParameters = new EncryptionParameters(SchemeType.BFV)
            {
                PolyModulusDegree = 32768,
                CoeffModulus      = CoeffModulus.BFVDefault(32768)
            };

            //encryptionParameters.PlainModulus = new Modulus(1024);

            /*
             * To enable batching, we need to set the plain_modulus to be a prime number
             * congruent to 1 modulo 2*PolyModulusDegree. Microsoft SEAL provides a helper
             * method for finding such a prime. In this example we create a 20-bit prime
             * that supports batching.
             */
            encryptionParameters.PlainModulus = PlainModulus.Batching(32768, 20);


            SEALContext new_context = new SEALContext(encryptionParameters);

            return(new_context);
        }
Ejemplo n.º 2
0
        /*
         * In `1_BFV_Basics.cs' we showed how to perform a very simple computation using the
         * BFV scheme. The computation was performed modulo the PlainModulus parameter, and
         * utilized only one coefficient from a BFV plaintext polynomial. This approach has
         * two notable problems:
         *
         *  (1) Practical applications typically use integer or real number arithmetic,
         *      not modular arithmetic;
         *  (2) We used only one coefficient of the plaintext polynomial. This is really
         *      wasteful, as the plaintext polynomial is large and will in any case be
         *      encrypted in its entirety.
         *
         * For (1), one may ask why not just increase the PlainModulus parameter until no
         * overflow occurs, and the computations behave as in integer arithmetic. The problem
         * is that increasing PlainModulus increases noise budget consumption, and decreases
         * the initial noise budget too.
         *
         * In these examples we will discuss other ways of laying out data into plaintext
         * elements (encoding) that allow more computations without data type overflow, and
         * can allow the full plaintext polynomial to be utilized.
         */
        private static void ExampleBatchEncoder()
        {
            Utilities.PrintExampleBanner("Example: Encoders / Batch Encoder");

            /*
             * [BatchEncoder] (For BFV scheme only)
             *
             * Let N denote the PolyModulusDegree and T denote the PlainModulus. Batching
             * allows the BFV plaintext polynomials to be viewed as 2-by-(N/2) matrices, with
             * each element an integer modulo T. In the matrix view, encrypted operations act
             * element-wise on encrypted matrices, allowing the user to obtain speeds-ups of
             * several orders of magnitude in fully vectorizable computations. Thus, in all
             * but the simplest computations, batching should be the preferred method to use
             * with BFV, and when used properly will result in implementations outperforming
             * anything done without batching.
             */
            using EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV);
            ulong polyModulusDegree = 8192;

            parms.PolyModulusDegree = polyModulusDegree;
            parms.CoeffModulus      = CoeffModulus.BFVDefault(polyModulusDegree);

            /*
             * To enable batching, we need to set the plain_modulus to be a prime number
             * congruent to 1 modulo 2*PolyModulusDegree. Microsoft SEAL provides a helper
             * method for finding such a prime. In this example we create a 20-bit prime
             * that supports batching.
             */
            parms.PlainModulus = PlainModulus.Batching(polyModulusDegree, 20);

            using SEALContext context = new SEALContext(parms);
            Utilities.PrintParameters(context);
            Console.WriteLine();

            /*
             * We can verify that batching is indeed enabled by looking at the encryption
             * parameter qualifiers created by SEALContext.
             */
            using var qualifiers = context.FirstContextData.Qualifiers;
            Console.WriteLine($"Batching enabled: {qualifiers.UsingBatching}");

            using KeyGenerator keygen = new KeyGenerator(context);
            using SecretKey secretKey = keygen.SecretKey;
            keygen.CreatePublicKey(out PublicKey publicKey);
            keygen.CreateRelinKeys(out RelinKeys relinKeys);
            using Encryptor encryptor = new Encryptor(context, publicKey);
            using Evaluator evaluator = new Evaluator(context);
            using Decryptor decryptor = new Decryptor(context, secretKey);

            /*
             * Batching is done through an instance of the BatchEncoder class.
             */
            using BatchEncoder batchEncoder = new BatchEncoder(context);

            /*
             * The total number of batching `slots' equals the PolyModulusDegree, N, and
             * these slots are organized into 2-by-(N/2) matrices that can be encrypted and
             * computed on. Each slot contains an integer modulo PlainModulus.
             */
            ulong slotCount = batchEncoder.SlotCount;
            ulong rowSize   = slotCount / 2;

            Console.WriteLine($"Plaintext matrix row size: {rowSize}");

            /*
             * The matrix plaintext is simply given to BatchEncoder as a flattened vector
             * of numbers. The first `rowSize' many numbers form the first row, and the
             * rest form the second row. Here we create the following matrix:
             *
             *  [ 0,  1,  2,  3,  0,  0, ...,  0 ]
             *  [ 4,  5,  6,  7,  0,  0, ...,  0 ]
             */
            ulong[] podMatrix = new ulong[slotCount];
            podMatrix[0]           = 0;
            podMatrix[1]           = 1;
            podMatrix[2]           = 2;
            podMatrix[3]           = 3;
            podMatrix[rowSize]     = 4;
            podMatrix[rowSize + 1] = 5;
            podMatrix[rowSize + 2] = 6;
            podMatrix[rowSize + 3] = 7;

            Console.WriteLine("Input plaintext matrix:");
            Utilities.PrintMatrix(podMatrix, (int)rowSize);

            /*
             * First we use BatchEncoder to encode the matrix into a plaintext polynomial.
             */
            using Plaintext plainMatrix = new Plaintext();
            Utilities.PrintLine();
            Console.WriteLine("Encode plaintext matrix:");
            batchEncoder.Encode(podMatrix, plainMatrix);

            /*
             * We can instantly decode to verify correctness of the encoding. Note that no
             * encryption or decryption has yet taken place.
             */
            List <ulong> podResult = new List <ulong>();

            Console.WriteLine("    + Decode plaintext matrix ...... Correct.");
            batchEncoder.Decode(plainMatrix, podResult);
            Utilities.PrintMatrix(podResult, (int)rowSize);

            /*
             * Next we encrypt the encoded plaintext.
             */
            using Ciphertext encryptedMatrix = new Ciphertext();
            Utilities.PrintLine();
            Console.WriteLine("Encrypt plainMatrix to encryptedMatrix.");
            encryptor.Encrypt(plainMatrix, encryptedMatrix);
            Console.WriteLine("    + Noise budget in encryptedMatrix: {0} bits",
                              decryptor.InvariantNoiseBudget(encryptedMatrix));

            /*
             * Operating on the ciphertext results in homomorphic operations being performed
             * simultaneously in all 8192 slots (matrix elements). To illustrate this, we
             * form another plaintext matrix
             *
             *  [ 1,  2,  1,  2,  1,  2, ..., 2 ]
             *  [ 1,  2,  1,  2,  1,  2, ..., 2 ]
             *
             * and encode it into a plaintext.
             */
            ulong[] podMatrix2 = new ulong[slotCount];
            for (ulong i = 0; i < slotCount; i++)
            {
                podMatrix2[i] = (i & 1) + 1;
            }
            using Plaintext plainMatrix2 = new Plaintext();
            batchEncoder.Encode(podMatrix2, plainMatrix2);
            Console.WriteLine();
            Console.WriteLine("Second input plaintext matrix:");
            Utilities.PrintMatrix(podMatrix2, (int)rowSize);

            /*
             * We now add the second (plaintext) matrix to the encrypted matrix, and square
             * the sum.
             */
            Utilities.PrintLine();
            Console.WriteLine("Sum, square, and relinearize.");
            evaluator.AddPlainInplace(encryptedMatrix, plainMatrix2);
            evaluator.SquareInplace(encryptedMatrix);
            evaluator.RelinearizeInplace(encryptedMatrix, relinKeys);

            /*
             * How much noise budget do we have left?
             */
            Console.WriteLine("    + Noise budget in result: {0} bits",
                              decryptor.InvariantNoiseBudget(encryptedMatrix));

            /*
             * We decrypt and decompose the plaintext to recover the result as a matrix.
             */
            using Plaintext plainResult = new Plaintext();
            Utilities.PrintLine();
            Console.WriteLine("Decrypt and decode result.");
            decryptor.Decrypt(encryptedMatrix, plainResult);
            batchEncoder.Decode(plainResult, podResult);
            Console.WriteLine("    + Result plaintext matrix ...... Correct.");
            Utilities.PrintMatrix(podResult, (int)rowSize);

            /*
             * Batching allows us to efficiently use the full plaintext polynomial when the
             * desired encrypted computation is highly parallelizable. However, it has not
             * solved the other problem mentioned in the beginning of this file: each slot
             * holds only an integer modulo plain_modulus, and unless plain_modulus is very
             * large, we can quickly encounter data type overflow and get unexpected results
             * when integer computations are desired. Note that overflow cannot be detected
             * in encrypted form. The CKKS scheme (and the CKKSEncoder) addresses the data
             * type overflow issue, but at the cost of yielding only approximate results.
             */
        }
Ejemplo n.º 3
0
        /*
         * Both the BFV scheme (with BatchEncoder) as well as the CKKS scheme support native
         * vectorized computations on encrypted numbers. In addition to computing slot-wise,
         * it is possible to rotate the encrypted vectors cyclically.
         */
        private static void ExampleRotationBFV()
        {
            Utilities.PrintExampleBanner("Example: Rotation / Rotation in BFV");

            using EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV);

            ulong polyModulusDegree = 8192;

            parms.PolyModulusDegree = polyModulusDegree;
            parms.CoeffModulus      = CoeffModulus.BFVDefault(polyModulusDegree);
            parms.PlainModulus      = PlainModulus.Batching(polyModulusDegree, 20);

            using SEALContext context = new SEALContext(parms);
            Utilities.PrintParameters(context);
            Console.WriteLine();

            using KeyGenerator keygen = new KeyGenerator(context);
            using SecretKey secretKey = keygen.SecretKey;
            keygen.CreatePublicKey(out PublicKey publicKey);
            keygen.CreateRelinKeys(out RelinKeys relinKeys);
            using Encryptor encryptor = new Encryptor(context, publicKey);
            using Evaluator evaluator = new Evaluator(context);
            using Decryptor decryptor = new Decryptor(context, secretKey);

            using BatchEncoder batchEncoder = new BatchEncoder(context);
            ulong slotCount = batchEncoder.SlotCount;
            ulong rowSize   = slotCount / 2;

            Console.WriteLine($"Plaintext matrix row size: {rowSize}");

            ulong[] podMatrix = new ulong[slotCount];
            podMatrix[0]           = 0;
            podMatrix[1]           = 1;
            podMatrix[2]           = 2;
            podMatrix[3]           = 3;
            podMatrix[rowSize]     = 4;
            podMatrix[rowSize + 1] = 5;
            podMatrix[rowSize + 2] = 6;
            podMatrix[rowSize + 3] = 7;

            Console.WriteLine("Input plaintext matrix:");
            Utilities.PrintMatrix(podMatrix, (int)rowSize);
            Console.WriteLine();

            /*
             * First we use BatchEncoder to encode the matrix into a plaintext. We encrypt
             * the plaintext as usual.
             */
            Utilities.PrintLine();
            using Plaintext plainMatrix = new Plaintext();
            Console.WriteLine("Encode and encrypt.");
            batchEncoder.Encode(podMatrix, plainMatrix);
            using Ciphertext encryptedMatrix = new Ciphertext();
            encryptor.Encrypt(plainMatrix, encryptedMatrix);
            Console.WriteLine("    + Noise budget in fresh encryption: {0} bits",
                              decryptor.InvariantNoiseBudget(encryptedMatrix));
            Console.WriteLine();

            /*
             * Rotations require yet another type of special key called `Galois keys'. These
             * are easily obtained from the KeyGenerator.
             */
            keygen.CreateGaloisKeys(out GaloisKeys galoisKeys);

            /*
             * Now rotate both matrix rows 3 steps to the left, decrypt, decode, and print.
             */
            Utilities.PrintLine();
            Console.WriteLine("Rotate rows 3 steps left.");
            evaluator.RotateRowsInplace(encryptedMatrix, 3, galoisKeys);
            using Plaintext plainResult = new Plaintext();
            Console.WriteLine("    + Noise budget after rotation: {0} bits",
                              decryptor.InvariantNoiseBudget(encryptedMatrix));
            Console.WriteLine("    + Decrypt and decode ...... Correct.");
            decryptor.Decrypt(encryptedMatrix, plainResult);
            List <ulong> podResult = new List <ulong>();

            batchEncoder.Decode(plainResult, podResult);
            Utilities.PrintMatrix(podResult, (int)rowSize);

            /*
             * We can also rotate the columns, i.e., swap the rows.
             */
            Utilities.PrintLine();
            Console.WriteLine("Rotate columns.");
            evaluator.RotateColumnsInplace(encryptedMatrix, galoisKeys);
            Console.WriteLine("    + Noise budget after rotation: {0} bits",
                              decryptor.InvariantNoiseBudget(encryptedMatrix));
            Console.WriteLine("    + Decrypt and decode ...... Correct.");
            decryptor.Decrypt(encryptedMatrix, plainResult);
            batchEncoder.Decode(plainResult, podResult);
            Utilities.PrintMatrix(podResult, (int)rowSize);

            /*
             * Finally, we rotate the rows 4 steps to the right, decrypt, decode, and print.
             */
            Utilities.PrintLine();
            Console.WriteLine("Rotate rows 4 steps right.");
            evaluator.RotateRowsInplace(encryptedMatrix, -4, galoisKeys);
            Console.WriteLine("    + Noise budget after rotation: {0} bits",
                              decryptor.InvariantNoiseBudget(encryptedMatrix));
            Console.WriteLine("    + Decrypt and decode ...... Correct.");
            decryptor.Decrypt(encryptedMatrix, plainResult);
            batchEncoder.Decode(plainResult, podResult);
            Utilities.PrintMatrix(podResult, (int)rowSize);

            /*
             * Note that rotations do not consume any noise budget. However, this is only
             * the case when the special prime is at least as large as the other primes. The
             * same holds for relinearization. Microsoft SEAL does not require that the
             * special prime is of any particular size, so ensuring this is the case is left
             * for the user to do.
             */
        }
Ejemplo n.º 4
0
        private static void ExampleLevels()
        {
            Utilities.PrintExampleBanner("Example: Levels");

            /*
             * In this examples we describe the concept of `levels' in BFV and CKKS and the
             * related objects that represent them in Microsoft SEAL.
             *
             * In Microsoft SEAL a set of encryption parameters (excluding the random number
             * generator) is identified uniquely by a 256-bit hash of the parameters. This
             * hash is called the `ParmsId' and can be easily accessed and printed at any
             * time. The hash will change as soon as any of the parameters is changed.
             *
             * When a SEALContext is created from a given EncryptionParameters instance,
             * Microsoft SEAL automatically creates a so-called `modulus switching chain',
             * which is a chain of other encryption parameters derived from the original set.
             * The parameters in the modulus switching chain are the same as the original
             * parameters with the exception that size of the coefficient modulus is
             * decreasing going down the chain. More precisely, each parameter set in the
             * chain attempts to remove the last coefficient modulus prime from the
             * previous set; this continues until the parameter set is no longer valid
             * (e.g., PlainModulus is larger than the remaining CoeffModulus). It is easy
             * to walk through the chain and access all the parameter sets. Additionally,
             * each parameter set in the chain has a `chain index' that indicates its
             * position in the chain so that the last set has index 0. We say that a set
             * of encryption parameters, or an object carrying those encryption parameters,
             * is at a higher level in the chain than another set of parameters if its the
             * chain index is bigger, i.e., it is earlier in the chain.
             *
             * Each set of parameters in the chain involves unique pre-computations performed
             * when the SEALContext is created, and stored in a SEALContext.ContextData
             * object. The chain is basically a linked list of SEALContext.ContextData
             * objects, and can easily be accessed through the SEALContext at any time. Each
             * node can be identified by the ParmsId of its specific encryption parameters
             * (PolyModulusDegree remains the same but CoeffModulus varies).
             */
            using EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV);
            ulong polyModulusDegree = 8192;

            parms.PolyModulusDegree = polyModulusDegree;

            /*
             * In this example we use a custom CoeffModulus, consisting of 5 primes of
             * sizes 50, 30, 30, 50, and 50 bits. Note that this is still OK according to
             * the explanation in `1_BFV_Basics.cs'. Indeed,
             *
             *  CoeffModulus.MaxBitCount(polyModulusDegree)
             *
             * returns 218 (greater than 50+30+30+50+50=210).
             *
             * Due to the modulus switching chain, the order of the 5 primes is significant.
             * The last prime has a special meaning and we call it the `special prime'. Thus,
             * the first parameter set in the modulus switching chain is the only one that
             * involves the special prime. All key objects, such as SecretKey, are created
             * at this highest level. All data objects, such as Ciphertext, can be only at
             * lower levels. The special modulus should be as large as the largest of the
             * other primes in the CoeffModulus, although this is not a strict requirement.
             *
             *       special prime +---------+
             |
             |                               v
             | CoeffModulus: { 50, 30, 30, 50, 50 }  +---+  Level 4 (all keys; `key level')
             |
             |
             |  CoeffModulus: { 50, 30, 30, 50 }  +---+  Level 3 (highest `data level')
             |
             |
             |      CoeffModulus: { 50, 30, 30 }  +---+  Level 2
             |
             |
             |          CoeffModulus: { 50, 30 }  +---+  Level 1
             |
             |
             |              CoeffModulus: { 50 }  +---+  Level 0 (lowest level)
             */
            parms.CoeffModulus = CoeffModulus.Create(
                polyModulusDegree, new int[] { 50, 30, 30, 50, 50 });

            /*
             * In this example the PlainModulus does not play much of a role; we choose
             * some reasonable value.
             */
            parms.PlainModulus = PlainModulus.Batching(polyModulusDegree, 20);

            using SEALContext context = new SEALContext(parms);
            Utilities.PrintParameters(context);

            /*
             * There are convenience method for accessing the SEALContext.ContextData for
             * some of the most important levels:
             *
             *  SEALContext.KeyContextData: access to key level ContextData
             *  SEALContext.FirstContextData: access to highest data level ContextData
             *  SEALContext.LastContextData: access to lowest level ContextData
             *
             * We iterate over the chain and print the ParmsId for each set of parameters.
             */
            Console.WriteLine();
            Utilities.PrintLine();
            Console.WriteLine("Print the modulus switching chain.");

            /*
             * First print the key level parameter information.
             */
            SEALContext.ContextData contextData = context.KeyContextData;
            Console.WriteLine("----> Level (chain index): {0} ...... KeyContextData",
                              contextData.ChainIndex);
            Console.WriteLine($"      ParmsId: {contextData.ParmsId}");
            Console.Write("      CoeffModulus primes: ");
            foreach (Modulus prime in contextData.Parms.CoeffModulus)
            {
                Console.Write($"{Utilities.ULongToString(prime.Value)} ");
            }
            Console.WriteLine();
            Console.WriteLine("\\");
            Console.Write(" \\--> ");

            /*
             * Next iterate over the remaining (data) levels.
             */
            contextData = context.FirstContextData;
            while (null != contextData)
            {
                Console.Write($"Level (chain index): {contextData.ChainIndex}");
                if (contextData.ParmsId.Equals(context.FirstParmsId))
                {
                    Console.WriteLine(" ...... FirstContextData");
                }
                else if (contextData.ParmsId.Equals(context.LastParmsId))
                {
                    Console.WriteLine(" ...... LastContextData");
                }
                else
                {
                    Console.WriteLine();
                }
                Console.WriteLine($"      ParmsId: {contextData.ParmsId}");
                Console.Write("      CoeffModulus primes: ");
                foreach (Modulus prime in contextData.Parms.CoeffModulus)
                {
                    Console.Write($"{Utilities.ULongToString(prime.Value)} ");
                }
                Console.WriteLine();
                Console.WriteLine("\\");
                Console.Write(" \\--> ");

                /*
                 * Step forward in the chain.
                 */
                contextData = contextData.NextContextData;
            }
            Console.WriteLine("End of chain reached");
            Console.WriteLine();

            /*
             * We create some keys and check that indeed they appear at the highest level.
             */
            using KeyGenerator keygen = new KeyGenerator(context);
            using SecretKey secretKey = keygen.SecretKey;
            keygen.CreatePublicKey(out PublicKey publicKey);
            keygen.CreateRelinKeys(out RelinKeys relinKeys);

            Utilities.PrintLine();
            Console.WriteLine("Print the parameter IDs of generated elements.");
            Console.WriteLine($"    + publicKey:  {publicKey.ParmsId}");
            Console.WriteLine($"    + secretKey:  {secretKey.ParmsId}");
            Console.WriteLine($"    + relinKeys:  {relinKeys.ParmsId}");

            using Encryptor encryptor = new Encryptor(context, publicKey);
            using Evaluator evaluator = new Evaluator(context);
            using Decryptor decryptor = new Decryptor(context, secretKey);

            /*
             * In the BFV scheme plaintexts do not carry a ParmsId, but ciphertexts do. Note
             * how the freshly encrypted ciphertext is at the highest data level.
             */
            using Plaintext plain      = new Plaintext("1x^3 + 2x^2 + 3x^1 + 4");
            using Ciphertext encrypted = new Ciphertext();
            encryptor.Encrypt(plain, encrypted);
            Console.WriteLine($"    + plain:      {plain.ParmsId} (not set in BFV)");
            Console.WriteLine($"    + encrypted:  {encrypted.ParmsId}");
            Console.WriteLine();

            /*
             * `Modulus switching' is a technique of changing the ciphertext parameters down
             * in the chain. The function Evaluator.ModSwitchToNext always switches to the
             * next level down the chain, whereas Evaluator.ModSwitchTo switches to a parameter
             * set down the chain corresponding to a given ParmsId. However, it is impossible
             * to switch up in the chain.
             */
            Utilities.PrintLine();
            Console.WriteLine("Perform modulus switching on encrypted and print.");
            contextData = context.FirstContextData;
            Console.Write("----> ");
            while (null != contextData.NextContextData)
            {
                Console.WriteLine($"Level (chain index): {contextData.ChainIndex}");
                Console.WriteLine($"      ParmsId of encrypted: {contextData.ParmsId}");
                Console.WriteLine("      Noise budget at this level: {0} bits",
                                  decryptor.InvariantNoiseBudget(encrypted));
                Console.WriteLine("\\");
                Console.Write(" \\--> ");
                evaluator.ModSwitchToNextInplace(encrypted);
                contextData = contextData.NextContextData;
            }
            Console.WriteLine($"Level (chain index): {contextData.ChainIndex}");
            Console.WriteLine($"      ParmsId of encrypted: {contextData.ParmsId}");
            Console.WriteLine("      Noise budget at this level: {0} bits",
                              decryptor.InvariantNoiseBudget(encrypted));
            Console.WriteLine("\\");
            Console.Write(" \\--> ");
            Console.WriteLine("End of chain reached");
            Console.WriteLine();

            /*
             * At this point it is hard to see any benefit in doing this: we lost a huge
             * amount of noise budget (i.e., computational power) at each switch and seemed
             * to get nothing in return. Decryption still works.
             */
            Utilities.PrintLine();
            Console.WriteLine("Decrypt still works after modulus switching.");
            decryptor.Decrypt(encrypted, plain);
            Console.WriteLine($"    + Decryption of encrypted: {plain} ...... Correct.");
            Console.WriteLine();

            /*
             * However, there is a hidden benefit: the size of the ciphertext depends
             * linearly on the number of primes in the coefficient modulus. Thus, if there
             * is no need or intention to perform any further computations on a given
             * ciphertext, we might as well switch it down to the smallest (last) set of
             * parameters in the chain before sending it back to the secret key holder for
             * decryption.
             *
             * Also the lost noise budget is actually not an issue at all, if we do things
             * right, as we will see below.
             *
             * First we recreate the original ciphertext and perform some computations.
             */
            Console.WriteLine("Computation is more efficient with modulus switching.");
            Utilities.PrintLine();
            Console.WriteLine("Compute the eight power.");
            encryptor.Encrypt(plain, encrypted);
            Console.WriteLine("    + Noise budget fresh:                  {0} bits",
                              decryptor.InvariantNoiseBudget(encrypted));
            evaluator.SquareInplace(encrypted);
            evaluator.RelinearizeInplace(encrypted, relinKeys);
            Console.WriteLine("    + Noise budget of the 2nd power:        {0} bits",
                              decryptor.InvariantNoiseBudget(encrypted));
            evaluator.SquareInplace(encrypted);
            evaluator.RelinearizeInplace(encrypted, relinKeys);
            Console.WriteLine("    + Noise budget of the 4th power:        {0} bits",
                              decryptor.InvariantNoiseBudget(encrypted));

            /*
             * Surprisingly, in this case modulus switching has no effect at all on the
             * noise budget.
             */
            evaluator.ModSwitchToNextInplace(encrypted);
            Console.WriteLine("    + Noise budget after modulus switching: {0} bits",
                              decryptor.InvariantNoiseBudget(encrypted));


            /*
             * This means that there is no harm at all in dropping some of the coefficient
             * modulus after doing enough computations. In some cases one might want to
             * switch to a lower level slightly earlier, actually sacrificing some of the
             * noise budget in the process, to gain computational performance from having
             * smaller parameters. We see from the print-out that the next modulus switch
             * should be done ideally when the noise budget is down to around 25 bits.
             */
            evaluator.SquareInplace(encrypted);
            evaluator.RelinearizeInplace(encrypted, relinKeys);
            Console.WriteLine("    + Noise budget of the 8th power:        {0} bits",
                              decryptor.InvariantNoiseBudget(encrypted));
            evaluator.ModSwitchToNextInplace(encrypted);
            Console.WriteLine("    + Noise budget after modulus switching: {0} bits",
                              decryptor.InvariantNoiseBudget(encrypted));

            /*
             * At this point the ciphertext still decrypts correctly, has very small size,
             * and the computation was as efficient as possible. Note that the decryptor
             * can be used to decrypt a ciphertext at any level in the modulus switching
             * chain.
             */
            decryptor.Decrypt(encrypted, plain);
            Console.WriteLine("    + Decryption of the 8th power (hexadecimal) ...... Correct.");
            Console.WriteLine($"    {plain}");
            Console.WriteLine();

            /*
             * In BFV modulus switching is not necessary and in some cases the user might
             * not want to create the modulus switching chain, except for the highest two
             * levels. This can be done by passing a bool `false' to SEALContext constructor.
             */
            using SEALContext context2 = new SEALContext(parms, expandModChain: false);

            /*
             * We can check that indeed the modulus switching chain has been created only
             * for the highest two levels (key level and highest data level). The following
             * loop should execute only once.
             */
            Console.WriteLine("Optionally disable modulus switching chain expansion.");
            Utilities.PrintLine();
            Console.WriteLine("Print the modulus switching chain.");
            Console.Write("----> ");
            for (contextData = context2.KeyContextData; null != contextData;
                 contextData = contextData.NextContextData)
            {
                Console.WriteLine($"Level (chain index): {contextData.ChainIndex}");
                Console.WriteLine($"      ParmsId of encrypted: {contextData.ParmsId}");
                Console.Write("      CoeffModulus primes: ");
                foreach (Modulus prime in contextData.Parms.CoeffModulus)
                {
                    Console.Write($"{Utilities.ULongToString(prime.Value)} ");
                }
                Console.WriteLine();
                Console.WriteLine("\\");
                Console.Write(" \\--> ");
            }
            Console.WriteLine("End of chain reached");
            Console.WriteLine();

            /*
             * It is very important to understand how this example works since in the CKKS
             * scheme modulus switching has a much more fundamental purpose and the next
             * examples will be difficult to understand unless these basic properties are
             * totally clear.
             */
        }
Ejemplo n.º 5
0
        private static void ExampleBGVBasics()
        {
            Utilities.PrintExampleBanner("Example: BGV Basics");

            /*
             * As an example, we evaluate the degree 8 polynomial
             *
             *  x^8
             *
             * over an encrypted x over integers 1, 2, 3, 4. The coefficients of the
             * polynomial can be considered as plaintext inputs, as we will see below. The
             * computation is done modulo the plain_modulus 1032193.
             *
             * Computing over encrypted data in the BGV scheme is similar to that in BFV.
             * The purpose of this example is mainly to explain the differences between BFV
             * and BGV in terms of ciphertext coefficient modulus selection and noise control.
             *
             * Most of the following code are repeated from "BFV basics" and "encoders" examples.
             */

            /*
             * Note that scheme_type is now "bgv".
             */
            using EncryptionParameters parms = new EncryptionParameters(SchemeType.BGV);
            ulong polyModulusDegree = 8192;

            parms.PolyModulusDegree = polyModulusDegree;

            /*
             * We can certainly use BFVDefault coeff_modulus. In later parts of this example,
             * we will demonstrate how to choose coeff_modulus that is more useful in BGV.
             */
            parms.CoeffModulus        = CoeffModulus.BFVDefault(polyModulusDegree);
            parms.PlainModulus        = PlainModulus.Batching(polyModulusDegree, 20);
            using SEALContext context = new SEALContext(parms);

            /*
             * Print the parameters that we have chosen.
             */
            Utilities.PrintLine();
            Console.WriteLine("Set encryption parameters and print");
            Utilities.PrintParameters(context);

            using KeyGenerator keygen = new KeyGenerator(context);
            using SecretKey secretKey = keygen.SecretKey;
            keygen.CreatePublicKey(out PublicKey publicKey);
            keygen.CreateRelinKeys(out RelinKeys relinKeys);
            using Encryptor encryptor = new Encryptor(context, publicKey);
            using Evaluator evaluator = new Evaluator(context);
            using Decryptor decryptor = new Decryptor(context, secretKey);

            /*
             * Batching and slot operations are the same in BFV and BGV.
             */
            using BatchEncoder batchEncoder = new BatchEncoder(context);
            ulong slotCount = batchEncoder.SlotCount;
            ulong rowSize   = slotCount / 2;

            Console.WriteLine($"Plaintext matrix row size: {rowSize}");

            /*
             * Here we create the following matrix:
             *  [ 1,  2,  3,  4,  0,  0, ...,  0 ]
             *  [ 0,  0,  0,  0,  0,  0, ...,  0 ]
             */
            ulong[] podMatrix = new ulong[slotCount];
            podMatrix[0] = 1;
            podMatrix[1] = 2;
            podMatrix[2] = 3;
            podMatrix[3] = 4;

            Console.WriteLine("Input plaintext matrix:");
            Utilities.PrintMatrix(podMatrix, (int)rowSize);
            using Plaintext xPlain = new Plaintext();
            Console.WriteLine("Encode plaintext matrix to xPlain:");
            batchEncoder.Encode(podMatrix, xPlain);

            /*
             * Next we encrypt the encoded plaintext.
             */
            using Ciphertext xEncrypted = new Ciphertext();
            Utilities.PrintLine();
            Console.WriteLine("Encrypt xPlain to xEncrypted.");
            encryptor.Encrypt(xPlain, xEncrypted);
            Console.WriteLine("    + noise budget in freshly encrypted x: {0} bits",
                              decryptor.InvariantNoiseBudget(xEncrypted));
            Console.WriteLine();

            /*
             * Then we compute x^2.
             */
            Utilities.PrintLine();
            Console.WriteLine("Compute and relinearize xSquared (x^2),");
            using Ciphertext xSquared = new Ciphertext();
            evaluator.Square(xEncrypted, xSquared);
            Console.WriteLine($"    + size of xSquared: {xSquared.Size}");
            evaluator.RelinearizeInplace(xSquared, relinKeys);
            Console.WriteLine("    + size of xSquared (after relinearization): {0}",
                              xSquared.Size);
            Console.WriteLine("    + noise budget in xSquared: {0} bits",
                              decryptor.InvariantNoiseBudget(xSquared));
            using Plaintext decryptedResult = new Plaintext();
            decryptor.Decrypt(xSquared, decryptedResult);
            List <ulong> podResult = new List <ulong>();

            batchEncoder.Decode(decryptedResult, podResult);
            Console.WriteLine("    + result plaintext matrix ...... Correct.");
            Utilities.PrintMatrix(podResult, (int)rowSize);

            /*
             * Next we compute x^4.
             */
            Utilities.PrintLine();
            Console.WriteLine("Compute and relinearize x4th (x^4),");
            using Ciphertext x4th = new Ciphertext();
            evaluator.Square(xSquared, x4th);
            Console.WriteLine($"    + size of x4th: {x4th.Size}");
            evaluator.RelinearizeInplace(x4th, relinKeys);
            Console.WriteLine("    + size of x4th (after relinearization): {0}",
                              x4th.Size);
            Console.WriteLine("    + noise budget in x4th: {0} bits",
                              decryptor.InvariantNoiseBudget(x4th));
            decryptor.Decrypt(x4th, decryptedResult);
            batchEncoder.Decode(decryptedResult, podResult);
            Console.WriteLine("    + result plaintext matrix ...... Correct.");
            Utilities.PrintMatrix(podResult, (int)rowSize);

            /*
             * Last we compute x^8. We run out of noise budget.
             */
            Utilities.PrintLine();
            Console.WriteLine("Compute and relinearize x8th (x^8),");
            using Ciphertext x8th = new Ciphertext();
            evaluator.Square(x4th, x8th);
            Console.WriteLine($"    + size of x8th: {x8th.Size}");
            evaluator.RelinearizeInplace(x8th, relinKeys);
            Console.WriteLine("    + size of x8th (after relinearization): {0}",
                              x8th.Size);
            Console.WriteLine("    + noise budget in x8th: {0} bits",
                              decryptor.InvariantNoiseBudget(x8th));
            Console.WriteLine("NOTE: Notice the increase in remaining noise budget.");

            Console.WriteLine();
            Console.WriteLine("~~~~~~ Use modulus switching to calculate x^8. ~~~~~~");

            /*
             * Noise budget has reached 0, which means that decryption cannot be expected
             * to give the correct result. BGV requires modulus switching to reduce noise
             * growth. In the following demonstration, we will insert a modulus switching
             * after each relinearization.
             */
            Utilities.PrintLine();
            Console.WriteLine("Encrypt xPlain to xEncrypted.");
            encryptor.Encrypt(xPlain, xEncrypted);
            Console.WriteLine("    + noise budget in freshly encrypted x: {0} bits",
                              decryptor.InvariantNoiseBudget(xEncrypted));
            Console.WriteLine();

            /*
             * Then we compute x^2.
             */
            Utilities.PrintLine();
            Console.WriteLine("Compute and relinearize xSquared (x^2),");
            Console.WriteLine("    + noise budget in xSquared (previously): {0} bits",
                              decryptor.InvariantNoiseBudget(xSquared));
            evaluator.Square(xEncrypted, xSquared);
            evaluator.RelinearizeInplace(xSquared, relinKeys);
            evaluator.ModSwitchToNextInplace(xSquared);
            Console.WriteLine("    + noise budget in xSquared (with modulus switching): {0} bits",
                              decryptor.InvariantNoiseBudget(xSquared));
            decryptor.Decrypt(xSquared, decryptedResult);
            batchEncoder.Decode(decryptedResult, podResult);
            Console.WriteLine("    + result plaintext matrix ...... Correct.");
            Utilities.PrintMatrix(podResult, (int)rowSize);

            /*
             * Next we compute x^4.
             */
            Utilities.PrintLine();
            Console.WriteLine("Compute and relinearize x4th (x^4),");
            Console.WriteLine("    + noise budget in x4th (previously): {0} bits",
                              decryptor.InvariantNoiseBudget(x4th));
            evaluator.Square(xSquared, x4th);
            evaluator.RelinearizeInplace(x4th, relinKeys);
            evaluator.ModSwitchToNextInplace(x4th);
            Console.WriteLine("    + noise budget in x4th (with modulus switching): {0} bits",
                              decryptor.InvariantNoiseBudget(x4th));
            decryptor.Decrypt(x4th, decryptedResult);
            batchEncoder.Decode(decryptedResult, podResult);
            Console.WriteLine("    + result plaintext matrix ...... Correct.");
            Utilities.PrintMatrix(podResult, (int)rowSize);

            /*
             * Last we compute x^8. We still have budget left.
             */
            Utilities.PrintLine();
            Console.WriteLine("Compute and relinearize x8th (x^8),");
            Console.WriteLine("    + noise budget in x8th (previously): {0} bits",
                              decryptor.InvariantNoiseBudget(x8th));
            evaluator.Square(x4th, x8th);
            evaluator.RelinearizeInplace(x8th, relinKeys);
            evaluator.ModSwitchToNextInplace(x8th);
            Console.WriteLine("    + noise budget in x8th (with modulus switching): {0} bits",
                              decryptor.InvariantNoiseBudget(x8th));
            decryptor.Decrypt(x8th, decryptedResult);
            batchEncoder.Decode(decryptedResult, podResult);
            Console.WriteLine("    + result plaintext matrix ...... Correct.");
            Utilities.PrintMatrix(podResult, (int)rowSize);

            /*
             * Although with modulus switching x_squared has less noise budget than before,
             * noise budget is consumed at a slower rate. To achieve the optimal consumption
             * rate of noise budget in an application, one needs to carefully choose the
             * location to insert modulus switching and manually choose coeff_modulus.
             */
        }