1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT license.
3 
4 using Microsoft.Research.SEAL.Tools;
5 using System;
6 using System.Text;
7 
8 namespace Microsoft.Research.SEAL
9 {
10     /// <summary>
11     /// Performs sanity checks (validation) and pre-computations for a given set of encryption
12     /// parameters.
13     /// </summary>
14     ///
15     /// <remarks>
16     /// <para>
17     /// Performs sanity checks (validation) and pre-computations for a given set of encryption
18     /// parameters. While the EncryptionParameters class is intended to be a light-weight class
19     /// to store the encryption parameters, the SEALContext class is a heavy-weight class that
20     /// is constructed from a given set of encryption parameters. It validates the parameters
21     /// for correctness, evaluates their properties, and performs and stores the results of
22     /// several costly pre-computations.
23     /// </para>
24     /// <para>
25     /// After the user has set at least the PolyModulus, CoeffModulus, and PlainModulus
26     /// parameters in a given EncryptionParameters instance, the parameters can be validated
27     /// for correctness and functionality by constructing an instance of SEALContext. The
28     /// constructor of SEALContext does all of its work automatically, and concludes by
29     /// constructing and storing an instance of the EncryptionParameterQualifiers class, with
30     /// its flags set according to the properties of the given parameters. If the created
31     /// instance of EncryptionParameterQualifiers has the ParametersSet flag set to true, the
32     /// given parameter set has been deemed valid and is ready to be used. If the parameters
33     /// were for some reason not appropriately set, the ParametersSet flag will be false,
34     /// and a new SEALContext will have to be created after the parameters are corrected.
35     /// </para>
36     /// <para>
37     /// By default, SEALContext creates a chain of SEALContext.ContextData instances. The
38     /// first one in the chain corresponds to special encryption parameters that are reserved
39     /// to be used by the various key classes (SecretKey, PublicKey, etc.). These are the
40     /// exact same encryption parameters that are created by the user and passed to the
41     /// constructor of SEALContext. The properties KeyContextData and KeyParmsId return the
42     /// ContextData and the ParmsId corresponding to these special parameters. The rest of the
43     /// ContextData instances in the chain correspond to encryption parameters that are derived
44     /// from the first encryption parameters by always removing the last one of the moduli in
45     /// the CoeffModulus, until the resulting parameters are no longer valid, e.g., there are
46     /// no more primes left. These derived encryption parameters are used by ciphertexts and
47     /// plaintexts and their respective ContextData can be accessed through the
48     /// GetContextData(ParmsId) function. The properties FirstContextData and LastContextData
49     /// return the ContextData corresponding to the first and the last set of parameters in
50     /// the "data" part of the chain, i.e., the second and the last element in the full chain.
51     /// The chain is a doubly linked list and is referred to as the modulus switching chain.
52     /// </para>
53     /// </remarks>
54     /// <see cref="EncryptionParameters">see EncryptionParameters for more details on the parameters.</see>
55     /// <see cref="EncryptionParameterQualifiers">see EncryptionParameterQualifiers for more details on the qualifiers.</see>
56     public class SEALContext : NativeObject
57     {
58         /// <summary>
59         /// Creates an instance of SEALContext and performs several pre-computations
60         /// on the given EncryptionParameters.
61         /// </summary>
62         /// <param name="parms">The encryption parameters.</param>
63         /// <param name="expandModChain">Determines whether the modulus switching chain
64         /// should be created</param>
65         /// <param name="secLevel">Determines whether a specific security level should be
66         /// enforced according to HomomorphicEncryption.org security standard</param>
67         /// <exception cref="ArgumentNullException">if parms is null</exception>
SEALContext(EncryptionParameters parms, bool expandModChain = true, SecLevelType secLevel = SecLevelType.TC128)68         public SEALContext(EncryptionParameters parms,
69             bool expandModChain = true, SecLevelType secLevel = SecLevelType.TC128)
70         {
71             if (null == parms)
72                 throw new ArgumentNullException(nameof(parms));
73 
74             NativeMethods.SEALContext_Create(parms.NativePtr,
75                 expandModChain, (int)secLevel, out IntPtr context);
76             NativePtr = context;
77         }
78 
79         /// <summary>
80         /// Returns the ContextData corresponding to encryption parameters with a given
81         /// parmsId. If parameters with the given ParmsId are not found then the function
82         /// returns null.
83         /// </summary>
84         ///
85         /// <param name="parmsId">The ParmsId of the encryption parameters</param>
86         /// <exception cref="ArgumentNullException">if parmsId is null</exception>
GetContextData(ParmsId parmsId)87         public ContextData GetContextData(ParmsId parmsId)
88         {
89             if (null == parmsId)
90                 throw new ArgumentNullException(nameof(parmsId));
91 
92             NativeMethods.SEALContext_GetContextData(NativePtr, parmsId.Block, out IntPtr contextData);
93             if (IntPtr.Zero.Equals(contextData))
94                 return null;
95 
96             ContextData data = new ContextData(contextData, owned: false);
97             return data;
98         }
99 
100         /// <summary>
101         /// Returns the ContextData corresponding to encryption parameters that are
102         /// used for keys.
103         /// </summary>
104         public ContextData KeyContextData
105         {
106             get
107             {
108                 NativeMethods.SEALContext_KeyContextData(NativePtr, out IntPtr contextData);
109                 ContextData data = new ContextData(contextData, owned: false);
110                 return data;
111             }
112         }
113 
114         /// <summary>
115         /// Returns the ContextData corresponding to the first encryption parameters
116         /// that are used for data.
117         /// </summary>
118         public ContextData FirstContextData
119         {
120             get
121             {
122                 NativeMethods.SEALContext_FirstContextData(NativePtr, out IntPtr contextData);
123                 ContextData data = new ContextData(contextData, owned: false);
124                 return data;
125             }
126         }
127 
128         /// <summary>
129         /// Returns the ContextData corresponding to the last encryption parameters
130         /// that are used for data.
131         /// </summary>
132         public ContextData LastContextData
133         {
134             get
135             {
136                 NativeMethods.SEALContext_LastContextData(NativePtr, out IntPtr contextData);
137                 ContextData data = new ContextData(contextData, owned: false);
138                 return data;
139             }
140         }
141 
142         /// <summary>
143         /// Returns whether the encryption parameters are valid.
144         /// </summary>
145         public bool ParametersSet
146         {
147             get
148             {
149                 NativeMethods.SEALContext_ParametersSet(NativePtr, out bool paramsSet);
150                 return paramsSet;
151             }
152         }
153 
154         /// <summary>
155         /// If the encryption parameters are set in a way that is considered valid by SEAL, return "success".
156         /// If the encryption parameters are set but not validated yet, return "none".
157         /// Otherwise, return a brief reason.
158         /// </summary>
ParameterErrorName()159         public string ParameterErrorName()
160         {
161             NativeMethods.SEALContext_ParameterErrorName(NativePtr, null, out ulong length);
162             byte[] buffer = new byte[length];
163             NativeMethods.SEALContext_ParameterErrorName(NativePtr, buffer, out length);
164             return Encoding.ASCII.GetString(buffer);
165         }
166 
167         /// <summary>
168         /// If the encryption parameters are set in a way that is considered valid by SEAL, return "valid".
169         /// Otherwise, return a comprehensive reason.
170         /// </summary>
ParameterErrorMessage()171         public string ParameterErrorMessage()
172         {
173             NativeMethods.SEALContext_ParameterErrorMessage(NativePtr, null, out ulong length);
174             byte[] buffer = new byte[length];
175             NativeMethods.SEALContext_ParameterErrorMessage(NativePtr, buffer, out length);
176             return Encoding.ASCII.GetString(buffer);
177         }
178 
179         /// <summary>
180         /// Returns a ParmsId corresponding to the set of encryption parameters
181         /// that are used for keys.
182         /// </summary>
183         public ParmsId KeyParmsId
184         {
185             get
186             {
187                 ParmsId parms = new ParmsId();
188                 NativeMethods.SEALContext_KeyParmsId(NativePtr, parms.Block);
189                 return parms;
190             }
191         }
192 
193         /// <summary>
194         /// Returns a ParmsId corresponding to the first encryption parameters that
195         /// are used for data.
196         /// </summary>
197         public ParmsId FirstParmsId
198         {
199             get
200             {
201                 ParmsId parms = new ParmsId();
202                 NativeMethods.SEALContext_FirstParmsId(NativePtr, parms.Block);
203                 return parms;
204             }
205         }
206 
207         /// <summary>
208         /// Returns a ParmsId corresponding to the last encryption parameters that
209         /// are used for data.
210         /// </summary>
211         public ParmsId LastParmsId
212         {
213             get
214             {
215                 ParmsId parms = new ParmsId();
216                 NativeMethods.SEALContext_LastParmsId(NativePtr, parms.Block);
217                 return parms;
218             }
219         }
220 
221 
222         /// <summary>
223         /// Returns whether the coefficient modulus supports keyswitching.
224         /// </summary>
225         /// <remarks>
226         /// Returns whether the coefficient modulus supports keyswitching. In
227         /// practice, support for keyswitching is required by Evaluator.Relinearize,
228         /// Evaluator.ApplyGalois, and all rotation and conjugation operations.
229         /// For keyswitching to be available, the coefficient modulus parameter must
230         /// consist of at least two prime number factors.
231         /// </remarks>
232         public bool UsingKeyswitching
233         {
234             get
235             {
236                 NativeMethods.SEALContext_UsingKeyswitching(NativePtr, out bool result);
237                 return result;
238             }
239         }
240 
241         /// <summary>
242         /// Destroy native object.
243         /// </summary>
DestroyNativeObject()244         protected override void DestroyNativeObject()
245         {
246             NativeMethods.SEALContext_Destroy(NativePtr);
247         }
248 
249         /// <summary>
250         /// Class to hold pre-computation data for a given set of encryption parameters.
251         /// </summary>
252         public class ContextData : NativeObject
253         {
254             /// <summary>
255             /// Build a ContextData object from a native pointer.
256             /// </summary>
257             /// <param name="ptr">Pointer to native object</param>
258             /// <param name="owned">Whether this instance owns the native object</param>
ContextData(IntPtr ptr, bool owned = true)259             internal ContextData(IntPtr ptr, bool owned = true)
260                 : base(ptr, owned)
261             {
262             }
263 
264             /// <summary>
265             /// Returns a copy of the underlying encryption parameters.
266             /// </summary>
267             public EncryptionParameters Parms
268             {
269                 get
270                 {
271                     NativeMethods.ContextData_Parms(NativePtr, out IntPtr parms);
272                     return new EncryptionParameters(parms);
273                 }
274             }
275 
276             /// <summary>
277             /// Returns the ParmsId of the current parameters.
278             /// </summary>
279             public ParmsId ParmsId
280             {
281                 get
282                 {
283                     return Parms.ParmsId;
284                 }
285             }
286 
287 
288             /// <summary>
289             /// Returns a copy of EncryptionParameterQualifiers corresponding to the
290             /// current encryption parameters.
291             /// </summary>
292             /// <remarks>
293             /// Returns a copy of EncryptionParameterQualifiers corresponding to the
294             /// current encryption parameters. Note that to change the qualifiers it is
295             /// necessary to create a new instance of SEALContext once appropriate changes
296             /// to the encryption parameters have been made.
297             /// </remarks>
298             public EncryptionParameterQualifiers Qualifiers
299             {
300                 get
301                 {
302                     NativeMethods.ContextData_Qualifiers(NativePtr, out IntPtr epq);
303                     EncryptionParameterQualifiers qualifiers = new EncryptionParameterQualifiers(epq);
304                     return qualifiers;
305                 }
306             }
307 
308             /// <summary>
309             /// Returns a the pre-computed product of all primes in the
310             /// coefficient modulus.
311             /// </summary>
312             /// <remarks>
313             /// Returns a the pre-computed product of all primes in the
314             /// coefficient modulus. The security of the encryption parameters largely depends
315             /// on the bit-length of this product, and on the degree of the polynomial modulus.
316             /// </remarks>
317             public ulong[] TotalCoeffModulus
318             {
319                 get
320                 {
321                     ulong count = 0;
322                     NativeMethods.ContextData_TotalCoeffModulus(NativePtr, ref count, null);
323 
324                     ulong[] result = new ulong[count];
325                     NativeMethods.ContextData_TotalCoeffModulus(NativePtr, ref count, result);
326 
327                     return result;
328                 }
329             }
330 
331             /// <summary>
332             /// Returns the significant bit count of the total coefficient modulus.
333             /// </summary>
334             public int TotalCoeffModulusBitCount
335             {
336                 get
337                 {
338                     NativeMethods.ContextData_TotalCoeffModulusBitCount(NativePtr, out int bitCount);
339                     return bitCount;
340                 }
341             }
342 
343             /// <summary>
344             /// Return a copy of BFV "Delta", i.e. coefficient modulus divided by
345             /// plaintext modulus.
346             /// </summary>
347             public ulong[] CoeffDivPlainModulus
348             {
349                 get
350                 {
351                     ulong count = 0;
352                     NativeMethods.ContextData_CoeffDivPlainModulus(NativePtr, ref count, null);
353 
354                     ulong[] cdpm = new ulong[count];
355                     NativeMethods.ContextData_CoeffDivPlainModulus(NativePtr, ref count, cdpm);
356 
357                     return cdpm;
358                 }
359             }
360 
361             /// <summary>
362             /// Return the threshold for the upper half of integers modulo PlainModulus.
363             /// This is simply(PlainModulus + 1) / 2.
364             /// </summary>
365             public ulong PlainUpperHalfThreshold
366             {
367                 get
368                 {
369                     NativeMethods.ContextData_PlainUpperHalfThreshold(NativePtr, out ulong puht);
370                     return puht;
371                 }
372             }
373 
374             /// <summary>
375             /// Return a copy of the plaintext upper half increment, i.e. coeffModulus
376             /// minus plainModulus.
377             /// </summary>
378             /// <remarks>
379             /// Return a copy of the plaintext upper half increment, i.e. coeffModulus
380             /// minus plainModulus. The upper half increment is represented as an integer
381             /// for the full product coeffModulus if UsingFastPlainLift is false and is
382             /// otherwise represented modulo each of the CoeffModulus primes in order.
383             /// </remarks>
384             public ulong[] PlainUpperHalfIncrement
385             {
386                 get
387                 {
388                     ulong count = 0;
389                     NativeMethods.ContextData_PlainUpperHalfIncrement(NativePtr, ref count, null);
390 
391                     ulong[] puhi = new ulong[count];
392                     NativeMethods.ContextData_PlainUpperHalfIncrement(NativePtr, ref count, puhi);
393 
394                     return puhi;
395                 }
396             }
397 
398             /// <summary>
399             /// Return a copy of the upper half threshold with respect to the total
400             /// coefficient modulus. This is needed in CKKS decryption.
401             /// </summary>
402             public ulong[] UpperHalfThreshold
403             {
404                 get
405                 {
406                     ulong count = 0;
407                     NativeMethods.ContextData_UpperHalfThreshold(NativePtr, ref count, null);
408 
409                     if (count == 0)
410                         return null;
411 
412                     ulong[] uht = new ulong[count];
413                     NativeMethods.ContextData_UpperHalfThreshold(NativePtr, ref count, uht);
414 
415                     return uht;
416                 }
417             }
418 
419             /// <summary>
420             /// Return a copy of the upper half increment used for computing Delta*m
421             /// and converting the coefficients to modulo CoeffModulus.
422             /// </summary>
423             /// <remarks>
424             /// Return a copy of the upper half increment used for computing Delta*m
425             /// and converting the coefficients to modulo CoeffModulus. For example,
426             /// t-1 in plaintext should change into
427             /// q - Delta = Delta*t + r_t(q) - Delta = Delta*(t-1) + r_t(q)
428             /// so multiplying the message by Delta is not enough and requires also an
429             /// addition of r_t(q). This is precisely the UpperHalfIncrement. Note that
430             /// this operation is only done for negative message coefficients, i.e. those
431             /// that exceed PlainUpperHalfThreshold.
432             /// </remarks>
433             public ulong[] UpperHalfIncrement
434             {
435                 get
436                 {
437                     ulong count = 0;
438                     NativeMethods.ContextData_UpperHalfIncrement(NativePtr, ref count, null);
439 
440                     if (count == 0)
441                         return null;
442 
443                     ulong[] uhi = new ulong[count];
444                     NativeMethods.ContextData_UpperHalfIncrement(NativePtr, ref count, uhi);
445 
446                     return uhi;
447                 }
448             }
449 
450             /// <summary>
451             /// Returns the context data corresponding to the previous parameters in the
452             /// modulus switching chain.
453             /// </summary>
454             /// <remarks>
455             /// Returns the context data corresponding to the previous parameters in the
456             /// modulus switching chain. If the current data is the first one in the chain,
457             /// then the result is nullptr.
458             /// </remarks>
459             public ContextData PrevContextData
460             {
461                 get
462                 {
463                     NativeMethods.ContextData_PrevContextData(NativePtr, out IntPtr prev);
464 
465                     if (IntPtr.Zero.Equals(prev))
466                         return null;
467 
468                     ContextData data = new ContextData(prev, owned: false);
469                     return data;
470                 }
471             }
472 
473             /// <summary>
474             /// Returns the context data corresponding to the next parameters in the modulus
475             /// switching chain.
476             /// </summary>
477             /// <remarks>
478             /// Returns the context data corresponding to the next parameters in the modulus
479             /// switching chain. If the current data is the last one in the chain, then the
480             /// result is nullptr.
481             /// </remarks>
482             public ContextData NextContextData
483             {
484                 get
485                 {
486                     NativeMethods.ContextData_NextContextData(NativePtr, out IntPtr next);
487 
488                     if (IntPtr.Zero.Equals(next))
489                         return null;
490 
491                     ContextData data = new ContextData(next, owned: false);
492                     return data;
493                 }
494             }
495 
496             /// <summary>
497             /// Returns the index of the parameter set in a chain.
498             /// </summary>
499             /// <remarks>
500             /// Returns the index of the parameter set in a chain. The initial parameters
501             /// have index 0 and the index increases sequentially in the parameter chain.
502             /// </remarks>
503             public ulong ChainIndex
504             {
505                 get
506                 {
507                     NativeMethods.ContextData_ChainIndex(NativePtr, out ulong index);
508                     return index;
509                 }
510             }
511 
512             /// <summary>
513             /// Destroy native object.
514             /// </summary>
DestroyNativeObject()515             protected override void DestroyNativeObject()
516             {
517                 NativeMethods.ContextData_Destroy(NativePtr);
518             }
519         }
520     }
521 }
522