1 // Copyright (c) Microsoft Corporation. All rights reserved. 2 // Licensed under the MIT license. 3 4 #pragma once 5 6 #include "seal/memorymanager.h" 7 #include "seal/modulus.h" 8 #include "seal/randomgen.h" 9 #include "seal/serialization.h" 10 #include "seal/version.h" 11 #include "seal/util/defines.h" 12 #include "seal/util/globals.h" 13 #include "seal/util/hash.h" 14 #include "seal/util/ztools.h" 15 #include <functional> 16 #include <iostream> 17 #include <memory> 18 #include <numeric> 19 20 namespace seal 21 { 22 /** 23 Describes the type of encryption scheme to be used. 24 */ 25 enum class scheme_type : std::uint8_t 26 { 27 // No scheme set; cannot be used for encryption 28 none = 0x0, 29 30 // Brakerski/Fan-Vercauteren scheme 31 bfv = 0x1, 32 33 // Cheon-Kim-Kim-Song scheme 34 ckks = 0x2 35 }; 36 37 /** 38 The data type to store unique identifiers of encryption parameters. 39 */ 40 using parms_id_type = util::HashFunction::hash_block_type; 41 42 /** 43 A parms_id_type value consisting of zeros. 44 */ 45 extern const parms_id_type parms_id_zero; 46 47 /** 48 Represents user-customizable encryption scheme settings. The parameters (most 49 importantly poly_modulus, coeff_modulus, plain_modulus) significantly affect 50 the performance, capabilities, and security of the encryption scheme. Once 51 an instance of EncryptionParameters is populated with appropriate parameters, 52 it can be used to create an instance of the SEALContext class, which verifies 53 the validity of the parameters, and performs necessary pre-computations. 54 55 Picking appropriate encryption parameters is essential to enable a particular 56 application while balancing performance and security. Some encryption settings 57 will not allow some inputs (e.g. attempting to encrypt a polynomial with more 58 coefficients than poly_modulus or larger coefficients than plain_modulus) or, 59 support the desired computations (with noise growing too fast due to too large 60 plain_modulus and too small coeff_modulus). 61 62 @par parms_id 63 The EncryptionParameters class maintains at all times a 256-bit hash of the 64 currently set encryption parameters called the parms_id. This hash acts as 65 a unique identifier of the encryption parameters and is used by all further 66 objects created for these encryption parameters. The parms_id is not intended 67 to be directly modified by the user but is used internally for pre-computation 68 data lookup and input validity checks. In modulus switching the user can use 69 the parms_id to keep track of the chain of encryption parameters. The parms_id 70 is not exposed in the public API of EncryptionParameters, but can be accessed 71 through the SEALContext::ContextData class once the SEALContext has been created. 72 73 @par Thread Safety 74 In general, reading from EncryptionParameters is thread-safe, while mutating 75 is not. 76 77 @warning Choosing inappropriate encryption parameters may lead to an encryption 78 scheme that is not secure, does not perform well, and/or does not support the 79 input and computation of the desired application. We highly recommend consulting 80 an expert in RLWE-based encryption when selecting parameters, as this is where 81 inexperienced users seem to most often make critical mistakes. 82 */ 83 class EncryptionParameters 84 { 85 friend class SEALContext; 86 87 friend struct std::hash<EncryptionParameters>; 88 89 public: 90 /** 91 Creates an empty set of encryption parameters. 92 93 @param[in] scheme The encryption scheme to be used 94 @see scheme_type for the supported schemes 95 */ 96 EncryptionParameters(scheme_type scheme = scheme_type::none) : scheme_(scheme) 97 { 98 compute_parms_id(); 99 } 100 101 /** 102 Creates an empty set of encryption parameters. 103 104 @param[in] scheme The encryption scheme to be used 105 @throws std::invalid_argument if scheme is not supported 106 */ 107 EncryptionParameters(std::uint8_t scheme) 108 { 109 // Check that a valid scheme is given 110 if (!is_valid_scheme(scheme)) 111 { 112 throw std::invalid_argument("unsupported scheme"); 113 } 114 115 scheme_ = static_cast<scheme_type>(scheme); 116 compute_parms_id(); 117 } 118 119 /** 120 Creates a copy of a given instance of EncryptionParameters. 121 122 @param[in] copy The EncryptionParameters to copy from 123 */ 124 EncryptionParameters(const EncryptionParameters ©) = default; 125 126 /** 127 Overwrites the EncryptionParameters instance with a copy of a given instance. 128 129 @param[in] assign The EncryptionParameters to copy from 130 */ 131 EncryptionParameters &operator=(const EncryptionParameters &assign) = default; 132 133 /** 134 Creates a new EncryptionParameters instance by moving a given instance. 135 136 @param[in] source The EncryptionParameters to move from 137 */ 138 EncryptionParameters(EncryptionParameters &&source) = default; 139 140 /** 141 Overwrites the EncryptionParameters instance by moving a given instance. 142 143 @param[in] assign The EncryptionParameters to move from 144 */ 145 EncryptionParameters &operator=(EncryptionParameters &&assign) = default; 146 147 /** 148 Sets the degree of the polynomial modulus parameter to the specified value. 149 The polynomial modulus directly affects the number of coefficients in 150 plaintext polynomials, the size of ciphertext elements, the computational 151 performance of the scheme (bigger is worse), and the security level (bigger 152 is better). In Microsoft SEAL the degree of the polynomial modulus must be 153 a power of 2 (e.g. 1024, 2048, 4096, 8192, 16384, or 32768). 154 155 @param[in] poly_modulus_degree The new polynomial modulus degree 156 @throws std::logic_error if a valid scheme is not set and poly_modulus_degree 157 is non-zero 158 */ 159 inline void set_poly_modulus_degree(std::size_t poly_modulus_degree) 160 { 161 if (scheme_ == scheme_type::none && poly_modulus_degree) 162 { 163 throw std::logic_error("poly_modulus_degree is not supported for this scheme"); 164 } 165 166 // Set the degree 167 poly_modulus_degree_ = poly_modulus_degree; 168 169 // Re-compute the parms_id 170 compute_parms_id(); 171 } 172 173 /** 174 Sets the coefficient modulus parameter. The coefficient modulus consists 175 of a list of distinct prime numbers, and is represented by a vector of 176 Modulus objects. The coefficient modulus directly affects the size 177 of ciphertext elements, the amount of computation that the scheme can 178 perform (bigger is better), and the security level (bigger is worse). In 179 Microsoft SEAL each of the prime numbers in the coefficient modulus must 180 be at most 60 bits, and must be congruent to 1 modulo 2*poly_modulus_degree. 181 182 @param[in] coeff_modulus The new coefficient modulus 183 @throws std::logic_error if a valid scheme is not set and coeff_modulus is 184 is non-empty 185 @throws std::invalid_argument if size of coeff_modulus is invalid 186 */ 187 inline void set_coeff_modulus(const std::vector<Modulus> &coeff_modulus) 188 { 189 // Check that a scheme is set 190 if (scheme_ == scheme_type::none) 191 { 192 if (!coeff_modulus.empty()) 193 { 194 throw std::logic_error("coeff_modulus is not supported for this scheme"); 195 } 196 } 197 else if (coeff_modulus.size() > SEAL_COEFF_MOD_COUNT_MAX || coeff_modulus.size() < SEAL_COEFF_MOD_COUNT_MIN) 198 { 199 throw std::invalid_argument("coeff_modulus is invalid"); 200 } 201 202 coeff_modulus_ = coeff_modulus; 203 204 // Re-compute the parms_id 205 compute_parms_id(); 206 } 207 208 /** 209 Sets the plaintext modulus parameter. The plaintext modulus is an integer 210 modulus represented by the Modulus class. The plaintext modulus 211 determines the largest coefficient that plaintext polynomials can represent. 212 It also affects the amount of computation that the scheme can perform 213 (bigger is worse). In Microsoft SEAL the plaintext modulus can be at most 214 60 bits long, but can otherwise be any integer. Note, however, that some 215 features (e.g. batching) require the plaintext modulus to be of a particular 216 form. 217 218 @param[in] plain_modulus The new plaintext modulus 219 @throws std::logic_error if scheme is not scheme_type::BFV and plain_modulus 220 is non-zero 221 */ 222 inline void set_plain_modulus(const Modulus &plain_modulus) 223 { 224 // Check that scheme is BFV 225 if (scheme_ != scheme_type::bfv && !plain_modulus.is_zero()) 226 { 227 throw std::logic_error("plain_modulus is not supported for this scheme"); 228 } 229 230 plain_modulus_ = plain_modulus; 231 232 // Re-compute the parms_id 233 compute_parms_id(); 234 } 235 236 /** 237 Sets the plaintext modulus parameter. The plaintext modulus is an integer 238 modulus represented by the Modulus class. This constructor instead 239 takes a std::uint64_t and automatically creates the Modulus object. 240 The plaintext modulus determines the largest coefficient that plaintext 241 polynomials can represent. It also affects the amount of computation that 242 the scheme can perform (bigger is worse). In Microsoft SEAL the plaintext 243 modulus can be at most 60 bits long, but can otherwise be any integer. Note, 244 however, that some features (e.g. batching) require the plaintext modulus 245 to be of a particular form. 246 247 @param[in] plain_modulus The new plaintext modulus 248 @throws std::invalid_argument if plain_modulus is invalid 249 */ 250 inline void set_plain_modulus(std::uint64_t plain_modulus) 251 { 252 set_plain_modulus(Modulus(plain_modulus)); 253 } 254 255 /** 256 Sets the random number generator factory to use for encryption. By default, 257 the random generator is set to UniformRandomGeneratorFactory::default_factory(). 258 Setting this value allows a user to specify a custom random number generator 259 source. 260 261 @param[in] random_generator Pointer to the random generator factory 262 */ 263 inline void set_random_generator(std::shared_ptr<UniformRandomGeneratorFactory> random_generator) noexcept 264 { 265 random_generator_ = std::move(random_generator); 266 } 267 268 /** 269 Returns the encryption scheme type. 270 */ 271 SEAL_NODISCARD inline scheme_type scheme() const noexcept 272 { 273 return scheme_; 274 } 275 276 /** 277 Returns the degree of the polynomial modulus parameter. 278 */ 279 SEAL_NODISCARD inline std::size_t poly_modulus_degree() const noexcept 280 { 281 return poly_modulus_degree_; 282 } 283 284 /** 285 Returns a const reference to the currently set coefficient modulus parameter. 286 */ 287 SEAL_NODISCARD inline auto coeff_modulus() const noexcept -> const std::vector<Modulus> & 288 { 289 return coeff_modulus_; 290 } 291 292 /** 293 Returns a const reference to the currently set plaintext modulus parameter. 294 */ 295 SEAL_NODISCARD inline const Modulus &plain_modulus() const noexcept 296 { 297 return plain_modulus_; 298 } 299 300 /** 301 Returns a pointer to the random number generator factory to use for encryption. 302 */ 303 SEAL_NODISCARD inline auto random_generator() const noexcept -> std::shared_ptr<UniformRandomGeneratorFactory> 304 { 305 return random_generator_; 306 } 307 308 /** 309 Compares a given set of encryption parameters to the current set of 310 encryption parameters. The comparison is performed by comparing the 311 parms_ids of the parameter sets rather than comparing the parameters 312 individually. 313 314 @parms[in] other The EncryptionParameters to compare against 315 */ 316 SEAL_NODISCARD inline bool operator==(const EncryptionParameters &other) const noexcept 317 { 318 return (parms_id_ == other.parms_id_); 319 } 320 321 /** 322 Compares a given set of encryption parameters to the current set of 323 encryption parameters. The comparison is performed by comparing 324 parms_ids of the parameter sets rather than comparing the parameters 325 individually. 326 327 @parms[in] other The EncryptionParameters to compare against 328 */ 329 SEAL_NODISCARD inline bool operator!=(const EncryptionParameters &other) const noexcept 330 { 331 return (parms_id_ != other.parms_id_); 332 } 333 334 /** 335 Returns an upper bound on the size of the EncryptionParameters, as if it 336 was written to an output stream. 337 338 @param[in] compr_mode The compression mode 339 @throws std::invalid_argument if the compression mode is not supported 340 @throws std::logic_error if the size does not fit in the return type 341 */ 342 SEAL_NODISCARD inline std::streamoff save_size( 343 compr_mode_type compr_mode = Serialization::compr_mode_default) const 344 { 345 std::size_t coeff_modulus_total_size = 346 coeff_modulus_.empty() 347 ? std::size_t(0) 348 : util::safe_cast<std::size_t>(coeff_modulus_[0].save_size(compr_mode_type::none)); 349 coeff_modulus_total_size = util::mul_safe(coeff_modulus_total_size, coeff_modulus_.size()); 350 351 std::size_t members_size = Serialization::ComprSizeEstimate( 352 util::add_safe( 353 sizeof(scheme_), 354 sizeof(std::uint64_t), // poly_modulus_degree_ 355 sizeof(std::uint64_t), // coeff_modulus_size 356 coeff_modulus_total_size, 357 util::safe_cast<std::size_t>(plain_modulus_.save_size(compr_mode_type::none))), 358 compr_mode); 359 360 return util::safe_cast<std::streamoff>(util::add_safe(sizeof(Serialization::SEALHeader), members_size)); 361 } 362 363 /** 364 Saves EncryptionParameters to an output stream. The output is in binary 365 format and is not human-readable. The output stream must have the "binary" 366 flag set. 367 368 @param[out] stream The stream to save the EncryptionParameters to 369 @param[in] compr_mode The desired compression mode 370 @throws std::invalid_argument if the compression mode is not supported 371 @throws std::logic_error if the data to be saved is invalid, or if 372 compression failed 373 @throws std::runtime_error if I/O operations failed 374 */ 375 inline std::streamoff save( 376 std::ostream &stream, compr_mode_type compr_mode = Serialization::compr_mode_default) const 377 { 378 using namespace std::placeholders; 379 return Serialization::Save( 380 std::bind(&EncryptionParameters::save_members, this, _1), save_size(compr_mode_type::none), stream, 381 compr_mode, false); 382 } 383 384 /** 385 Loads EncryptionParameters from an input stream overwriting the current 386 EncryptionParameters. 387 388 @param[in] stream The stream to load the EncryptionParameters from 389 @throws std::logic_error if the data cannot be loaded by this version of 390 Microsoft SEAL, if the loaded data is invalid or if decompression failed 391 @throws std::runtime_error if I/O operations failed 392 */ 393 inline std::streamoff load(std::istream &stream) 394 { 395 using namespace std::placeholders; 396 EncryptionParameters new_parms(scheme_type::none); 397 auto in_size = 398 Serialization::Load(std::bind(&EncryptionParameters::load_members, &new_parms, _1, _2), stream, false); 399 std::swap(*this, new_parms); 400 return in_size; 401 } 402 403 /** 404 Saves EncryptionParameters to a given memory location. The output is in 405 binary format and is not human-readable. 406 407 @param[out] out The memory location to write the EncryptionParameters to 408 @param[in] size The number of bytes available in the given memory location 409 @param[in] compr_mode The desired compression mode 410 @throws std::invalid_argument if out is null or if size is too small to 411 contain a SEALHeader, or if the compression mode is not supported 412 @throws std::logic_error if the data to be saved is invalid, or if 413 compression failed 414 @throws std::runtime_error if I/O operations failed 415 */ 416 inline std::streamoff save( 417 seal_byte *out, std::size_t size, compr_mode_type compr_mode = Serialization::compr_mode_default) const 418 { 419 using namespace std::placeholders; 420 return Serialization::Save( 421 std::bind(&EncryptionParameters::save_members, this, _1), save_size(compr_mode_type::none), out, size, 422 compr_mode, false); 423 } 424 425 /** 426 Loads EncryptionParameters from a given memory location overwriting the 427 current EncryptionParameters. 428 429 @param[in] in The memory location to load the EncryptionParameters from 430 @param[in] size The number of bytes available in the given memory location 431 @throws std::invalid_argument if in is null or if size is too small to 432 contain a SEALHeader 433 @throws std::logic_error if the data cannot be loaded by this version of 434 Microsoft SEAL, if the loaded data is invalid, or if decompression failed 435 @throws std::runtime_error if I/O operations failed 436 */ 437 inline std::streamoff load(const seal_byte *in, std::size_t size) 438 { 439 using namespace std::placeholders; 440 EncryptionParameters new_parms(scheme_type::none); 441 auto in_size = Serialization::Load( 442 std::bind(&EncryptionParameters::load_members, &new_parms, _1, _2), in, size, false); 443 std::swap(*this, new_parms); 444 return in_size; 445 } 446 447 /** 448 Enables access to private members of seal::EncryptionParameters for SEAL_C. 449 */ 450 struct EncryptionParametersPrivateHelper; 451 452 private: 453 /** 454 Helper function to determine whether given std::uint8_t represents a valid 455 value for scheme_type. The return value will be false is the scheme is set 456 to scheme_type::none. 457 */ 458 SEAL_NODISCARD bool is_valid_scheme(std::uint8_t scheme) const noexcept 459 { 460 switch (scheme) 461 { 462 case static_cast<std::uint8_t>(scheme_type::none): 463 /* fall through */ 464 465 case static_cast<std::uint8_t>(scheme_type::bfv): 466 /* fall through */ 467 468 case static_cast<std::uint8_t>(scheme_type::ckks): 469 return true; 470 } 471 return false; 472 } 473 474 /** 475 Returns the parms_id of the current parameters. This function is intended 476 for internal use. 477 */ 478 SEAL_NODISCARD inline auto &parms_id() const noexcept 479 { 480 return parms_id_; 481 } 482 483 void compute_parms_id(); 484 485 void save_members(std::ostream &stream) const; 486 487 void load_members(std::istream &stream, SEALVersion version); 488 489 MemoryPoolHandle pool_ = MemoryManager::GetPool(); 490 491 scheme_type scheme_; 492 493 std::size_t poly_modulus_degree_ = 0; 494 495 std::vector<Modulus> coeff_modulus_{}; 496 497 std::shared_ptr<UniformRandomGeneratorFactory> random_generator_{ nullptr }; 498 499 Modulus plain_modulus_{}; 500 501 parms_id_type parms_id_ = parms_id_zero; 502 }; 503 } // namespace seal 504 505 /** 506 Specializes the std::hash template for parms_id_type. 507 */ 508 namespace std 509 { 510 template <> 511 struct hash<seal::parms_id_type> 512 { 513 std::size_t operator()(const seal::parms_id_type &parms_id) const 514 { 515 std::uint64_t result = 17; 516 result = 31 * result + parms_id[0]; 517 result = 31 * result + parms_id[1]; 518 result = 31 * result + parms_id[2]; 519 result = 31 * result + parms_id[3]; 520 return static_cast<std::size_t>(result); 521 } 522 }; 523 524 template <> 525 struct hash<seal::EncryptionParameters> 526 { 527 std::size_t operator()(const seal::EncryptionParameters &parms) const 528 { 529 hash<seal::parms_id_type> parms_id_hash; 530 return parms_id_hash(parms.parms_id_); 531 } 532 }; 533 } // namespace std 534