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