1 // Copyright (c) Microsoft Corporation. All rights reserved. 2 // Licensed under the MIT license. 3 4 #pragma once 5 6 #include "seal/ciphertext.h" 7 #include "seal/context.h" 8 #include "seal/galoiskeys.h" 9 #include "seal/memorymanager.h" 10 #include "seal/modulus.h" 11 #include "seal/plaintext.h" 12 #include "seal/relinkeys.h" 13 #include "seal/secretkey.h" 14 #include "seal/valcheck.h" 15 #include "seal/util/iterator.h" 16 #include <map> 17 #include <stdexcept> 18 #include <vector> 19 20 namespace seal 21 { 22 /** 23 Provides operations on ciphertexts. Due to the properties of the encryption scheme, the arithmetic operations pass 24 through the encryption layer to the underlying plaintext, changing it according to the type of the operation. Since 25 the plaintext elements are fundamentally polynomials in the polynomial quotient ring Z_T[x]/(X^N+1), where T is the 26 plaintext modulus and X^N+1 is the polynomial modulus, this is the ring where the arithmetic operations will take 27 place. BatchEncoder (batching) provider an alternative possibly more convenient view of the plaintext elements as 28 2-by-(N2/2) matrices of integers modulo the plaintext modulus. In the batching view the arithmetic operations act on 29 the matrices element-wise. Some of the operations only apply in the batching view, such as matrix row and column 30 rotations. Other operations such as relinearization have no semantic meaning but are necessary for performance 31 reasons. 32 33 @par Arithmetic Operations 34 The core operations are arithmetic operations, in particular multiplication and addition of ciphertexts. In addition 35 to these, we also provide negation, subtraction, squaring, exponentiation, and multiplication and addition of 36 several ciphertexts for convenience. in many cases some of the inputs to a computation are plaintext elements rather 37 than ciphertexts. For this we provide fast "plain" operations: plain addition, plain subtraction, and plain 38 multiplication. 39 40 @par Relinearization 41 One of the most important non-arithmetic operations is relinearization, which takes as input a ciphertext of size 42 K+1 and relinearization keys (at least K-1 keys are needed), and changes the size of the ciphertext down to 2 43 (minimum size). For most use-cases only one relinearization key suffices, in which case relinearization should be 44 performed after every multiplication. Homomorphic multiplication of ciphertexts of size K+1 and L+1 outputs a 45 ciphertext of size K+L+1, and the computational cost of multiplication is proportional to K*L. Plain multiplication 46 and addition operations of any type do not change the size. Relinearization requires relinearization keys to have 47 been generated. 48 49 @par Rotations 50 When batching is enabled, we provide operations for rotating the plaintext matrix rows cyclically left or right, and 51 for rotating the columns (swapping the rows). Rotations require Galois keys to have been generated. 52 53 @par Other Operations 54 We also provide operations for transforming ciphertexts to NTT form and back, and for transforming plaintext 55 polynomials to NTT form. These can be used in a very fast plain multiplication variant, that assumes the inputs to 56 be in NTT form. Since the NTT has to be done in any case in plain multiplication, this function can be used when 57 e.g. one plaintext input is used in several plain multiplication, and transforming it several times would not make 58 sense. 59 60 @par NTT form 61 When using the BFV scheme (scheme_type::bfv), all plaintexts and ciphertexts should remain by default in the usual 62 coefficient representation, i.e., not in NTT form. When using the CKKS scheme (scheme_type::ckks), all plaintexts 63 and ciphertexts should remain by default in NTT form. We call these scheme-specific NTT states the "default NTT 64 form". Some functions, such as add, work even if the inputs are not in the default state, but others, such as 65 multiply, will throw an exception. The output of all evaluation functions will be in the same state as the input(s), 66 with the exception of the transform_to_ntt and transform_from_ntt functions, which change the state. Ideally, unless 67 these two functions are called, all other functions should "just work". 68 69 @see EncryptionParameters for more details on encryption parameters. 70 @see BatchEncoder for more details on batching 71 @see RelinKeys for more details on relinearization keys. 72 @see GaloisKeys for more details on Galois keys. 73 */ 74 class Evaluator 75 { 76 public: 77 /** 78 Creates an Evaluator instance initialized with the specified SEALContext. 79 80 @param[in] context The SEALContext 81 @throws std::invalid_argument if the encryption parameters are not valid 82 */ 83 Evaluator(const SEALContext &context); 84 85 /** 86 Negates a ciphertext. 87 88 @param[in] encrypted The ciphertext to negate 89 @throws std::invalid_argument if encrypted is not valid for the encryption 90 parameters 91 */ 92 void negate_inplace(Ciphertext &encrypted) const; 93 94 /** 95 Negates a ciphertext and stores the result in the destination parameter. 96 97 @param[in] encrypted The ciphertext to negate 98 @param[out] destination The ciphertext to overwrite with the negated result 99 @throws std::invalid_argument if encrypted is not valid for the encryption parameters 100 @throws std::logic_error if result ciphertext is transparent 101 */ negate(const Ciphertext & encrypted,Ciphertext & destination)102 inline void negate(const Ciphertext &encrypted, Ciphertext &destination) const 103 { 104 destination = encrypted; 105 negate_inplace(destination); 106 } 107 108 /** 109 Adds two ciphertexts. This function adds together encrypted1 and encrypted2 and stores the result in encrypted1. 110 111 @param[in] encrypted1 The first ciphertext to add 112 @param[in] encrypted2 The second ciphertext to add 113 @throws std::invalid_argument if encrypted1 or encrypted2 is not valid for the encryption parameters 114 @throws std::invalid_argument if encrypted1 and encrypted2 are in different NTT forms 115 @throws std::invalid_argument if encrypted1 and encrypted2 are at different level or scale 116 @throws std::logic_error if result ciphertext is transparent 117 */ 118 void add_inplace(Ciphertext &encrypted1, const Ciphertext &encrypted2) const; 119 120 /** 121 Adds two ciphertexts. This function adds together encrypted1 and encrypted2 and stores the result in the 122 destination parameter. 123 124 @param[in] encrypted1 The first ciphertext to add 125 @param[in] encrypted2 The second ciphertext to add 126 @param[out] destination The ciphertext to overwrite with the addition result 127 @throws std::invalid_argument if encrypted1 or encrypted2 is not valid for the encryption parameters 128 @throws std::invalid_argument if encrypted1 and encrypted2 are in different NTT forms 129 @throws std::invalid_argument if encrypted1 and encrypted2 are at different level or scale 130 @throws std::logic_error if result ciphertext is transparent 131 */ add(const Ciphertext & encrypted1,const Ciphertext & encrypted2,Ciphertext & destination)132 inline void add(const Ciphertext &encrypted1, const Ciphertext &encrypted2, Ciphertext &destination) const 133 { 134 if (&encrypted2 == &destination) 135 { 136 add_inplace(destination, encrypted1); 137 } 138 else 139 { 140 destination = encrypted1; 141 add_inplace(destination, encrypted2); 142 } 143 } 144 145 /** 146 Adds together a vector of ciphertexts and stores the result in the destination parameter. 147 148 @param[in] encrypteds The ciphertexts to add 149 @param[out] destination The ciphertext to overwrite with the addition result 150 @throws std::invalid_argument if encrypteds is empty 151 @throws std::invalid_argument if encrypteds are not valid for the encryption 152 parameters 153 @throws std::invalid_argument if encrypteds are in different NTT forms 154 @throws std::invalid_argument if encrypteds are at different level or scale 155 @throws std::invalid_argument if destination is one of encrypteds 156 @throws std::logic_error if result ciphertext is transparent 157 */ 158 void add_many(const std::vector<Ciphertext> &encrypteds, Ciphertext &destination) const; 159 160 /** 161 Subtracts two ciphertexts. This function computes the difference of encrypted1 and encrypted2, and stores the 162 result in encrypted1. 163 164 @param[in] encrypted1 The ciphertext to subtract from 165 @param[in] encrypted2 The ciphertext to subtract 166 @throws std::invalid_argument if encrypted1 or encrypted2 is not valid for the encryption parameters 167 @throws std::invalid_argument if encrypted1 and encrypted2 are in different NTT forms 168 @throws std::invalid_argument if encrypted1 and encrypted2 are at different level or scale 169 @throws std::logic_error if result ciphertext is transparent 170 */ 171 void sub_inplace(Ciphertext &encrypted1, const Ciphertext &encrypted2) const; 172 173 /** 174 Subtracts two ciphertexts. This function computes the difference of encrypted1 and encrypted2 and stores the 175 result in the destination parameter. 176 177 @param[in] encrypted1 The ciphertext to subtract from 178 @param[in] encrypted2 The ciphertext to subtract 179 @param[out] destination The ciphertext to overwrite with the subtraction result 180 @throws std::invalid_argument if encrypted1 or encrypted2 is not valid for the encryption parameters 181 @throws std::invalid_argument if encrypted1 and encrypted2 are in different NTT forms 182 @throws std::invalid_argument if encrypted1 and encrypted2 are at different level or scale 183 @throws std::logic_error if result ciphertext is transparent 184 */ sub(const Ciphertext & encrypted1,const Ciphertext & encrypted2,Ciphertext & destination)185 inline void sub(const Ciphertext &encrypted1, const Ciphertext &encrypted2, Ciphertext &destination) const 186 { 187 if (&encrypted2 == &destination) 188 { 189 sub_inplace(destination, encrypted1); 190 negate_inplace(destination); 191 } 192 else 193 { 194 destination = encrypted1; 195 sub_inplace(destination, encrypted2); 196 } 197 } 198 199 /** 200 Multiplies two ciphertexts. This functions computes the product of encrypted1 and encrypted2 and stores the 201 result in encrypted1. Dynamic memory allocations in the process are allocated from the memory pool pointed to by 202 the given MemoryPoolHandle. 203 204 @param[in] encrypted1 The first ciphertext to multiply 205 @param[in] encrypted2 The second ciphertext to multiply 206 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 207 @throws std::invalid_argument if encrypted1 or encrypted2 is not valid for the encryption parameters 208 @throws std::invalid_argument if encrypted1 or encrypted2 is not in the default NTT form 209 @throws std::invalid_argument if encrypted1 and encrypted2 are at different level 210 @throws std::invalid_argument if the output scale is too large for the encryption parameters 211 @throws std::invalid_argument if pool is uninitialized 212 @throws std::logic_error if result ciphertext is transparent 213 */ 214 void multiply_inplace( 215 Ciphertext &encrypted1, const Ciphertext &encrypted2, 216 MemoryPoolHandle pool = MemoryManager::GetPool()) const; 217 218 /** 219 Multiplies two ciphertexts. This functions computes the product of encrypted1 and encrypted2 and stores the 220 result in the destination parameter. Dynamic memory allocations in the process are allocated from the memory 221 pool pointed to by the given MemoryPoolHandle. 222 223 @param[in] encrypted1 The first ciphertext to multiply 224 @param[in] encrypted2 The second ciphertext to multiply 225 @param[out] destination The ciphertext to overwrite with the multiplication result 226 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 227 @throws std::invalid_argument if encrypted1 or encrypted2 is not valid for the encryption parameters 228 @throws std::invalid_argument if encrypted1 and encrypted2 are at different level 229 @throws std::invalid_argument if encrypted1 or encrypted2 is not in the default NTT form 230 @throws std::invalid_argument if the output scale is too large for the encryption parameters 231 @throws std::invalid_argument if pool is uninitialized 232 @throws std::logic_error if result ciphertext is transparent 233 */ 234 inline void multiply( 235 const Ciphertext &encrypted1, const Ciphertext &encrypted2, Ciphertext &destination, 236 MemoryPoolHandle pool = MemoryManager::GetPool()) const 237 { 238 if (&encrypted2 == &destination) 239 { 240 multiply_inplace(destination, encrypted1, std::move(pool)); 241 } 242 else 243 { 244 destination = encrypted1; 245 multiply_inplace(destination, encrypted2, std::move(pool)); 246 } 247 } 248 249 /** 250 Squares a ciphertext. This functions computes the square of encrypted. Dynamic memory allocations in the process 251 are allocated from the memory pool pointed to by the given MemoryPoolHandle. 252 253 @param[in] encrypted The ciphertext to square 254 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 255 @throws std::invalid_argument if encrypted is not valid for the encryption parameters 256 @throws std::invalid_argument if encrypted is not in the default NTT form 257 @throws std::invalid_argument if the output scale is too large for the encryption parameters 258 @throws std::invalid_argument if pool is uninitialized 259 @throws std::logic_error if result ciphertext is transparent 260 */ 261 void square_inplace(Ciphertext &encrypted, MemoryPoolHandle pool = MemoryManager::GetPool()) const; 262 263 /** 264 Squares a ciphertext. This functions computes the square of encrypted and stores the result in the destination 265 parameter. Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given 266 MemoryPoolHandle. 267 268 @param[in] encrypted The ciphertext to square 269 @param[out] destination The ciphertext to overwrite with the square 270 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 271 @throws std::invalid_argument if encrypted is not valid for the encryption parameters 272 @throws std::invalid_argument if encrypted is not in the default NTT form 273 @throws std::invalid_argument if the output scale is too large for the encryption parameters 274 @throws std::invalid_argument if pool is uninitialized 275 @throws std::logic_error if result ciphertext is transparent 276 */ 277 inline void square( 278 const Ciphertext &encrypted, Ciphertext &destination, 279 MemoryPoolHandle pool = MemoryManager::GetPool()) const 280 { 281 destination = encrypted; 282 square_inplace(destination, std::move(pool)); 283 } 284 285 /** 286 Relinearizes a ciphertext. This functions relinearizes encrypted, reducing its size down to 2. If the size of 287 encrypted is K+1, the given relinearization keys need to have size at least K-1. Dynamic memory allocations in 288 the process are allocated from the memory pool pointed to by the given MemoryPoolHandle. 289 290 @param[in] encrypted The ciphertext to relinearize 291 @param[in] relin_keys The relinearization keys 292 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 293 @throws std::invalid_argument if encrypted or relin_keys is not valid for the encryption parameters 294 @throws std::invalid_argument if encrypted is not in the default NTT form 295 @throws std::invalid_argument if relin_keys do not correspond to the top level parameters in the current context 296 @throws std::invalid_argument if the size of relin_keys is too small 297 @throws std::invalid_argument if pool is uninitialized 298 @throws std::logic_error if keyswitching is not supported by the context 299 @throws std::logic_error if result ciphertext is transparent 300 */ 301 inline void relinearize_inplace( 302 Ciphertext &encrypted, const RelinKeys &relin_keys, MemoryPoolHandle pool = MemoryManager::GetPool()) const 303 { 304 relinearize_internal(encrypted, relin_keys, 2, std::move(pool)); 305 } 306 307 /** 308 Relinearizes a ciphertext. This functions relinearizes encrypted, reducing its size down to 2, and stores the 309 result in the destination parameter. If the size of encrypted is K+1, the given relinearization keys need to 310 have size at least K-1. Dynamic memory allocations in the process are allocated from the memory pool pointed to 311 by the given MemoryPoolHandle. 312 313 @param[in] encrypted The ciphertext to relinearize 314 @param[in] relin_keys The relinearization keys 315 @param[out] destination The ciphertext to overwrite with the relinearized result 316 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 317 @throws std::invalid_argument if encrypted or relin_keys is not valid for the encryption parameters 318 @throws std::invalid_argument if encrypted is not in the default NTT form 319 @throws std::invalid_argument if relin_keys do not correspond to the top level parameters in the current context 320 @throws std::invalid_argument if the size of relin_keys is too small 321 @throws std::invalid_argument if pool is uninitialized 322 @throws std::logic_error if keyswitching is not supported by the context 323 @throws std::logic_error if result ciphertext is transparent 324 */ 325 inline void relinearize( 326 const Ciphertext &encrypted, const RelinKeys &relin_keys, Ciphertext &destination, 327 MemoryPoolHandle pool = MemoryManager::GetPool()) const 328 { 329 destination = encrypted; 330 relinearize_inplace(destination, relin_keys, std::move(pool)); 331 } 332 333 /** 334 Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1} and 335 stores the result in the destination parameter. Dynamic memory allocations in the process are allocated from the 336 memory pool pointed to by the given MemoryPoolHandle. 337 338 @param[in] encrypted The ciphertext to be switched to a smaller modulus 339 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 340 @param[out] destination The ciphertext to overwrite with the modulus switched result 341 @throws std::invalid_argument if encrypted is not valid for the encryption parameters 342 @throws std::invalid_argument if encrypted is not in the default NTT form 343 @throws std::invalid_argument if encrypted is already at lowest level 344 @throws std::invalid_argument if the scale is too large for the new encryption parameters 345 @throws std::invalid_argument if pool is uninitialized 346 @throws std::logic_error if result ciphertext is transparent 347 */ 348 void mod_switch_to_next( 349 const Ciphertext &encrypted, Ciphertext &destination, 350 MemoryPoolHandle pool = MemoryManager::GetPool()) const; 351 352 /** 353 Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1}. Dynamic 354 memory allocations in the process are allocated from the memory pool pointed to by the given MemoryPoolHandle. 355 356 @param[in] encrypted The ciphertext to be switched to a smaller modulus 357 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 358 @throws std::invalid_argument if encrypted is not valid for the encryption parameters 359 @throws std::invalid_argument if encrypted is not in the default NTT form 360 @throws std::invalid_argument if encrypted is already at lowest level 361 @throws std::invalid_argument if the scale is too large for the new encryption parameters 362 @throws std::invalid_argument if pool is uninitialized 363 @throws std::logic_error if result ciphertext is transparent 364 */ 365 inline void mod_switch_to_next_inplace( 366 Ciphertext &encrypted, MemoryPoolHandle pool = MemoryManager::GetPool()) const 367 { 368 mod_switch_to_next(encrypted, encrypted, std::move(pool)); 369 } 370 371 /** 372 Modulus switches an NTT transformed plaintext from modulo q_1...q_k down to modulo q_1...q_{k-1}. 373 374 @param[in] plain The plaintext to be switched to a smaller modulus 375 @throws std::invalid_argument if plain is not in NTT form 376 @throws std::invalid_argument if plain is not valid for the encryption parameters 377 @throws std::invalid_argument if plain is already at lowest level 378 @throws std::invalid_argument if the scale is too large for the new encryption parameters 379 */ mod_switch_to_next_inplace(Plaintext & plain)380 inline void mod_switch_to_next_inplace(Plaintext &plain) const 381 { 382 // Verify parameters. 383 if (!is_valid_for(plain, context_)) 384 { 385 throw std::invalid_argument("plain is not valid for encryption parameters"); 386 } 387 mod_switch_drop_to_next(plain); 388 } 389 390 /** 391 Modulus switches an NTT transformed plaintext from modulo q_1...q_k down to modulo q_1...q_{k-1} and stores the 392 result in the destination parameter. 393 394 @param[in] plain The plaintext to be switched to a smaller modulus 395 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 396 @param[out] destination The plaintext to overwrite with the modulus switched result 397 @throws std::invalid_argument if plain is not in NTT form 398 @throws std::invalid_argument if plain is not valid for the encryption parameters 399 @throws std::invalid_argument if plain is already at lowest level 400 @throws std::invalid_argument if the scale is too large for the new encryption parameters 401 @throws std::invalid_argument if pool is uninitialized 402 */ mod_switch_to_next(const Plaintext & plain,Plaintext & destination)403 inline void mod_switch_to_next(const Plaintext &plain, Plaintext &destination) const 404 { 405 destination = plain; 406 mod_switch_to_next_inplace(destination); 407 } 408 409 /** 410 Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters 411 reach the given parms_id. Dynamic memory allocations in the process are allocated from the memory pool pointed 412 to by the given MemoryPoolHandle. 413 414 @param[in] encrypted The ciphertext to be switched to a smaller modulus 415 @param[in] parms_id The target parms_id 416 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 417 @throws std::invalid_argument if encrypted is not valid for the encryption parameters 418 @throws std::invalid_argument if encrypted is not in the default NTT form 419 @throws std::invalid_argument if parms_id is not valid for the encryption parameters 420 @throws std::invalid_argument if encrypted is already at lower level in modulus chain than the parameters 421 corresponding to parms_id 422 @throws std::invalid_argument if the scale is too large for the new encryption parameters 423 @throws std::invalid_argument if pool is uninitialized 424 @throws std::logic_error if result ciphertext is transparent 425 */ 426 void mod_switch_to_inplace( 427 Ciphertext &encrypted, parms_id_type parms_id, MemoryPoolHandle pool = MemoryManager::GetPool()) const; 428 429 /** 430 Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters 431 reach the given parms_id and stores the result in the destination parameter. Dynamic memory allocations in the 432 process are allocated from the memory pool pointed to by the given MemoryPoolHandle. 433 434 @param[in] encrypted The ciphertext to be switched to a smaller modulus 435 @param[in] parms_id The target parms_id 436 @param[out] destination The ciphertext to overwrite with the modulus switched result 437 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 438 @throws std::invalid_argument if encrypted is not valid for the encryption parameters 439 @throws std::invalid_argument if encrypted is not in the default NTT form 440 @throws std::invalid_argument if parms_id is not valid for the encryption parameters 441 @throws std::invalid_argument if encrypted is already at lower level in modulus chain than the parameters 442 corresponding to parms_id 443 @throws std::invalid_argument if the scale is too large for the new encryption parameters 444 @throws std::invalid_argument if pool is uninitialized 445 @throws std::logic_error if result ciphertext is transparent 446 */ 447 inline void mod_switch_to( 448 const Ciphertext &encrypted, parms_id_type parms_id, Ciphertext &destination, 449 MemoryPoolHandle pool = MemoryManager::GetPool()) const 450 { 451 destination = encrypted; 452 mod_switch_to_inplace(destination, parms_id, std::move(pool)); 453 } 454 455 /** 456 Given an NTT transformed plaintext modulo q_1...q_k, this function switches the modulus down until the 457 parameters reach the given parms_id. 458 459 @param[in] plain The plaintext to be switched to a smaller modulus 460 @param[in] parms_id The target parms_id 461 @throws std::invalid_argument if plain is not in NTT form 462 @throws std::invalid_argument if plain is not valid for the encryption parameters 463 @throws std::invalid_argument if parms_id is not valid for the encryption parameters 464 @throws std::invalid_argument if plain is already at lower level in modulus chain than the parameters 465 corresponding to parms_id 466 @throws std::invalid_argument if the scale is too large for the new encryption parameters 467 */ 468 void mod_switch_to_inplace(Plaintext &plain, parms_id_type parms_id) const; 469 470 /** 471 Given an NTT transformed plaintext modulo q_1...q_k, this function switches the modulus down until the 472 parameters reach the given parms_id and stores the result in the destination parameter. 473 474 @param[in] plain The plaintext to be switched to a smaller modulus 475 @param[in] parms_id The target parms_id 476 @param[out] destination The plaintext to overwrite with the modulus switched result 477 @throws std::invalid_argument if plain is not in NTT form 478 @throws std::invalid_argument if plain is not valid for the encryption parameters 479 @throws std::invalid_argument if parms_id is not valid for the encryption parameters 480 @throws std::invalid_argument if plain is already at lower level in modulus chain than the parameters 481 corresponding to parms_id 482 @throws std::invalid_argument if the scale is too large for the new encryption parameters 483 */ mod_switch_to(const Plaintext & plain,parms_id_type parms_id,Plaintext & destination)484 inline void mod_switch_to(const Plaintext &plain, parms_id_type parms_id, Plaintext &destination) const 485 { 486 destination = plain; 487 mod_switch_to_inplace(destination, parms_id); 488 } 489 490 /** 491 Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1}, scales 492 the message down accordingly, and stores the result in the destination parameter. Dynamic memory allocations in 493 the process are allocated from the memory pool pointed to by the given MemoryPoolHandle. 494 495 @param[in] encrypted The ciphertext to be switched to a smaller modulus 496 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 497 @param[out] destination The ciphertext to overwrite with the modulus switched result 498 @throws std::invalid_argument if the scheme is invalid for rescaling 499 @throws std::invalid_argument if encrypted is not valid for the encryption parameters 500 @throws std::invalid_argument if encrypted is not in the default NTT form 501 @throws std::invalid_argument if encrypted is already at lowest level 502 @throws std::invalid_argument if pool is uninitialized 503 @throws std::logic_error if result ciphertext is transparent 504 */ 505 void rescale_to_next( 506 const Ciphertext &encrypted, Ciphertext &destination, 507 MemoryPoolHandle pool = MemoryManager::GetPool()) const; 508 509 /** 510 Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down to q_1...q_{k-1} and 511 scales the message down accordingly. Dynamic memory allocations in the process are allocated from the memory 512 pool pointed to by the given MemoryPoolHandle. 513 514 @param[in] encrypted The ciphertext to be switched to a smaller modulus 515 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 516 @throws std::invalid_argument if the scheme is invalid for rescaling 517 @throws std::invalid_argument if encrypted is not valid for the encryption parameters 518 @throws std::invalid_argument if encrypted is not in the default NTT form 519 @throws std::invalid_argument if encrypted is already at lowest level 520 @throws std::invalid_argument if pool is uninitialized 521 @throws std::logic_error if result ciphertext is transparent 522 */ 523 inline void rescale_to_next_inplace( 524 Ciphertext &encrypted, MemoryPoolHandle pool = MemoryManager::GetPool()) const 525 { 526 rescale_to_next(encrypted, encrypted, std::move(pool)); 527 } 528 529 /** 530 Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters 531 reach the given parms_id and scales the message down accordingly. Dynamic memory allocations in the process are 532 allocated from the memory pool pointed to by the given MemoryPoolHandle. 533 534 @param[in] encrypted The ciphertext to be switched to a smaller modulus 535 @param[in] parms_id The target parms_id 536 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 537 @throws std::invalid_argument if the scheme is invalid for rescaling 538 @throws std::invalid_argument if encrypted is not valid for the encryption parameters 539 @throws std::invalid_argument if encrypted is not in the default NTT form 540 @throws std::invalid_argument if parms_id is not valid for the encryption parameters 541 @throws std::invalid_argument if encrypted is already at lower level in modulus chain than the parameters 542 corresponding to parms_id 543 @throws std::invalid_argument if pool is uninitialized 544 @throws std::logic_error if result ciphertext is transparent 545 */ 546 void rescale_to_inplace( 547 Ciphertext &encrypted, parms_id_type parms_id, MemoryPoolHandle pool = MemoryManager::GetPool()) const; 548 549 /** 550 Given a ciphertext encrypted modulo q_1...q_k, this function switches the modulus down until the parameters 551 reach the given parms_id, scales the message down accordingly, and stores the result in the destination 552 parameter. Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given 553 MemoryPoolHandle. 554 555 @param[in] encrypted The ciphertext to be switched to a smaller modulus 556 @param[in] parms_id The target parms_id 557 @param[out] destination The ciphertext to overwrite with the modulus switched result 558 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 559 @throws std::invalid_argument if the scheme is invalid for rescaling 560 @throws std::invalid_argument if encrypted is not valid for the encryption parameters 561 @throws std::invalid_argument if encrypted is not in the default NTT form 562 @throws std::invalid_argument if parms_id is not valid for the encryption parameters 563 @throws std::invalid_argument if encrypted is already at lower level in modulus chain than the parameters 564 corresponding to parms_id 565 @throws std::invalid_argument if pool is uninitialized 566 @throws std::logic_error if result ciphertext is transparent 567 */ 568 inline void rescale_to( 569 const Ciphertext &encrypted, parms_id_type parms_id, Ciphertext &destination, 570 MemoryPoolHandle pool = MemoryManager::GetPool()) const 571 { 572 destination = encrypted; 573 rescale_to_inplace(destination, parms_id, std::move(pool)); 574 } 575 576 /** 577 Multiplies several ciphertexts together. This function computes the product of several ciphertext given as an 578 std::vector and stores the result in the destination parameter. The multiplication is done in a depth-optimal 579 order, and relinearization is performed automatically after every multiplication in the process. In 580 relinearization the given relinearization keys are used. Dynamic memory allocations in the process are allocated 581 from the memory pool pointed to by the given MemoryPoolHandle. 582 583 @param[in] encrypteds The ciphertexts to multiply 584 @param[in] relin_keys The relinearization keys 585 @param[out] destination The ciphertext to overwrite with the multiplication result 586 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 587 @throws std::logic_error if scheme is not scheme_type::bfv 588 @throws std::invalid_argument if encrypteds is empty 589 @throws std::invalid_argument if ciphertexts or relin_keys are not valid for the encryption parameters 590 @throws std::invalid_argument if encrypteds are not in the default NTT form 591 @throws std::invalid_argument if the output scale is too large for the encryption parameters 592 @throws std::invalid_argument if the size of relin_keys is too small 593 @throws std::invalid_argument if pool is uninitialized 594 @throws std::logic_error if keyswitching is not supported by the context 595 @throws std::logic_error if result ciphertext is transparent 596 */ 597 void multiply_many( 598 const std::vector<Ciphertext> &encrypteds, const RelinKeys &relin_keys, Ciphertext &destination, 599 MemoryPoolHandle pool = MemoryManager::GetPool()) const; 600 601 /** 602 Exponentiates a ciphertext. This functions raises encrypted to a power. Dynamic memory allocations in the 603 process are allocated from the memory pool pointed to by the given MemoryPoolHandle. The exponentiation is done 604 in a depth-optimal order, and relinearization is performed automatically after every multiplication in the 605 process. In relinearization the given relinearization keys are used. 606 607 @param[in] encrypted The ciphertext to exponentiate 608 @param[in] exponent The power to raise the ciphertext to 609 @param[in] relin_keys The relinearization keys 610 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 611 @throws std::logic_error if scheme is not scheme_type::bfv 612 @throws std::invalid_argument if encrypted or relin_keys is not valid for the encryption parameters 613 @throws std::invalid_argument if encrypted is not in the default NTT form 614 @throws std::invalid_argument if the output scale is too large for the encryption parameters 615 @throws std::invalid_argument if exponent is zero 616 @throws std::invalid_argument if the size of relin_keys is too small 617 @throws std::invalid_argument if pool is uninitialized 618 @throws std::logic_error if keyswitching is not supported by the context 619 @throws std::logic_error if result ciphertext is transparent 620 */ 621 void exponentiate_inplace( 622 Ciphertext &encrypted, std::uint64_t exponent, const RelinKeys &relin_keys, 623 MemoryPoolHandle pool = MemoryManager::GetPool()) const; 624 625 /** 626 Exponentiates a ciphertext. This functions raises encrypted to a power and stores the result in the destination 627 parameter. Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given 628 MemoryPoolHandle. The exponentiation is done in a depth-optimal order, and relinearization is performed 629 automatically after every multiplication in the process. In relinearization the given relinearization keys are 630 used. 631 632 @param[in] encrypted The ciphertext to exponentiate 633 @param[in] exponent The power to raise the ciphertext to 634 @param[in] relin_keys The relinearization keys 635 @param[out] destination The ciphertext to overwrite with the power 636 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 637 @throws std::logic_error if scheme is not scheme_type::bfv 638 @throws std::invalid_argument if encrypted or relin_keys is not valid for the encryption parameters 639 @throws std::invalid_argument if encrypted is not in the default NTT form 640 @throws std::invalid_argument if the output scale is too large for the encryption parameters 641 @throws std::invalid_argument if exponent is zero 642 @throws std::invalid_argument if the size of relin_keys is too small 643 @throws std::invalid_argument if pool is uninitialized 644 @throws std::logic_error if keyswitching is not supported by the context 645 @throws std::logic_error if result ciphertext is transparent 646 */ 647 inline void exponentiate( 648 const Ciphertext &encrypted, std::uint64_t exponent, const RelinKeys &relin_keys, Ciphertext &destination, 649 MemoryPoolHandle pool = MemoryManager::GetPool()) const 650 { 651 destination = encrypted; 652 exponentiate_inplace(destination, exponent, relin_keys, std::move(pool)); 653 } 654 655 /** 656 Adds a ciphertext and a plaintext. 657 658 @param[in] encrypted The ciphertext to add 659 @param[in] plain The plaintext to add 660 @throws std::invalid_argument if encrypted or plain is not valid for the encryption parameters 661 @throws std::invalid_argument if encrypted or plain is not in the default NTT form 662 @throws std::invalid_argument if encrypted and plain are at different level or scale 663 @throws std::logic_error if result ciphertext is transparent 664 */ 665 void add_plain_inplace(Ciphertext &encrypted, const Plaintext &plain) const; 666 667 /** 668 Adds a ciphertext and a plaintext. This function adds a ciphertext and a plaintext and stores the result in the 669 destination parameter. Note that in many cases it can be much more efficient to perform any computations on raw 670 unencrypted data before encoding it, rather than using this function to compute on the plaintext objects. 671 672 @param[in] encrypted The ciphertext to add 673 @param[in] plain The plaintext to add 674 @param[out] destination The ciphertext to overwrite with the addition result 675 @throws std::invalid_argument if encrypted or plain is not valid for the encryption parameters 676 @throws std::invalid_argument if encrypted or plain is not in the default NTT form 677 @throws std::invalid_argument if encrypted and plain are at different level or scale 678 @throws std::logic_error if result ciphertext is transparent 679 */ add_plain(const Ciphertext & encrypted,const Plaintext & plain,Ciphertext & destination)680 inline void add_plain(const Ciphertext &encrypted, const Plaintext &plain, Ciphertext &destination) const 681 { 682 destination = encrypted; 683 add_plain_inplace(destination, plain); 684 } 685 686 /** 687 Subtracts a plaintext from a ciphertext. 688 689 @param[in] encrypted The ciphertext to subtract from 690 @param[in] plain The plaintext to subtract 691 @throws std::invalid_argument if encrypted or plain is not valid for the encryption parameters 692 @throws std::invalid_argument if encrypted or plain is not in the default NTT form 693 @throws std::invalid_argument if encrypted and plain are at different level or scale 694 @throws std::logic_error if result ciphertext is transparent 695 */ 696 void sub_plain_inplace(Ciphertext &encrypted, const Plaintext &plain) const; 697 698 /** 699 Subtracts a plaintext from a ciphertext. This function subtracts a plaintext from a ciphertext and stores the 700 result in the destination parameter. 701 702 @param[in] encrypted The ciphertext to subtract from 703 @param[in] plain The plaintext to subtract 704 @param[out] destination The ciphertext to overwrite with the subtraction result 705 @throws std::invalid_argument if encrypted or plain is not valid for the encryption parameters 706 @throws std::invalid_argument if encrypted or plain is not in the default NTT form 707 @throws std::invalid_argument if encrypted and plain are at different level or scale 708 @throws std::logic_error if result ciphertext is transparent 709 */ sub_plain(const Ciphertext & encrypted,const Plaintext & plain,Ciphertext & destination)710 inline void sub_plain(const Ciphertext &encrypted, const Plaintext &plain, Ciphertext &destination) const 711 { 712 destination = encrypted; 713 sub_plain_inplace(destination, plain); 714 } 715 716 /** 717 Multiplies a ciphertext with a plaintext. The plaintext cannot be identically 0. Dynamic memory allocations in 718 the process are allocated from the memory pool pointed to by the given MemoryPoolHandle. 719 720 @param[in] encrypted The ciphertext to multiply 721 @param[in] plain The plaintext to multiply 722 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 723 @throws std::invalid_argument if encrypted or plain is not valid for the encryption parameters 724 @throws std::invalid_argument if encrypted and plain are in different NTT forms 725 @throws std::invalid_argument if the output scale is too large for the encryption parameters 726 @throws std::invalid_argument if pool is uninitialized 727 @throws std::logic_error if result ciphertext is transparent 728 */ 729 void multiply_plain_inplace( 730 Ciphertext &encrypted, const Plaintext &plain, MemoryPoolHandle pool = MemoryManager::GetPool()) const; 731 732 /** 733 Multiplies a ciphertext with a plaintext. This function multiplies a ciphertext with a plaintext and stores the 734 result in the destination parameter. The plaintext cannot be identically 0. Dynamic memory allocations in the 735 process are allocated from the memory pool pointed to by the given MemoryPoolHandle. 736 737 @param[in] encrypted The ciphertext to multiply 738 @param[in] plain The plaintext to multiply 739 @param[out] destination The ciphertext to overwrite with the multiplication result 740 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 741 @throws std::invalid_argument if encrypted or plain is not valid for the encryption parameters 742 @throws std::invalid_argument if encrypted and plain are in different NTT forms 743 @throws std::invalid_argument if the output scale is too large for the encryption parameters 744 @throws std::invalid_argument if pool is uninitialized 745 @throws std::logic_error if result ciphertext is transparent 746 */ 747 inline void multiply_plain( 748 const Ciphertext &encrypted, const Plaintext &plain, Ciphertext &destination, 749 MemoryPoolHandle pool = MemoryManager::GetPool()) const 750 { 751 destination = encrypted; 752 multiply_plain_inplace(destination, plain, std::move(pool)); 753 } 754 755 /** 756 Transforms a plaintext to NTT domain. This functions applies the Number Theoretic Transform to a plaintext by 757 first embedding integers modulo the plaintext modulus to integers modulo the coefficient modulus and then 758 performing David Harvey's NTT on the resulting polynomial. The transformation is done with respect to encryption 759 parameters corresponding to a given parms_id. For the operation to be valid, the plaintext must have degree less 760 than poly_modulus_degree and each coefficient must be less than the plaintext modulus, i.e., the plaintext must 761 be a valid plaintext under the current encryption parameters. Dynamic memory allocations in the process are 762 allocated from the memory pool pointed to by the given MemoryPoolHandle. 763 764 @param[in] plain The plaintext to transform 765 @param[in] parms_id The parms_id with respect to which the NTT is done 766 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 767 @throws std::invalid_argument if plain is already in NTT form 768 @throws std::invalid_argument if plain or parms_id is not valid for the 769 encryption parameters 770 @throws std::invalid_argument if pool is uninitialized 771 */ 772 void transform_to_ntt_inplace( 773 Plaintext &plain, parms_id_type parms_id, MemoryPoolHandle pool = MemoryManager::GetPool()) const; 774 775 /** 776 Transforms a plaintext to NTT domain. This functions applies the Number Theoretic Transform to a plaintext by 777 first embedding integers modulo the plaintext modulus to integers modulo the coefficient modulus and then 778 performing David Harvey's NTT on the resulting polynomial. The transformation is done with respect to encryption 779 parameters corresponding to a given parms_id. The result is stored in the destination_ntt parameter. For the 780 operation to be valid, the plaintext must have degree less than poly_modulus_degree and each coefficient must be 781 less than the plaintext modulus, i.e., the plaintext must be a valid plaintext under the current encryption 782 parameters. Dynamic memory allocations in the process are allocated from the memory pool pointed to by the given 783 MemoryPoolHandle. 784 785 @param[in] plain The plaintext to transform 786 @param[in] parms_id The parms_id with respect to which the NTT is done 787 @param[out] destinationNTT The plaintext to overwrite with the transformed result 788 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 789 @throws std::invalid_argument if plain is already in NTT form 790 @throws std::invalid_argument if plain or parms_id is not valid for the 791 encryption parameters 792 @throws std::invalid_argument if pool is uninitialized 793 */ 794 inline void transform_to_ntt( 795 const Plaintext &plain, parms_id_type parms_id, Plaintext &destination_ntt, 796 MemoryPoolHandle pool = MemoryManager::GetPool()) const 797 { 798 destination_ntt = plain; 799 transform_to_ntt_inplace(destination_ntt, parms_id, std::move(pool)); 800 } 801 802 /** 803 Transforms a ciphertext to NTT domain. This functions applies David Harvey's Number Theoretic Transform 804 separately to each polynomial of a ciphertext. 805 806 @param[in] encrypted The ciphertext to transform 807 @throws std::invalid_argument if encrypted is not valid for the encryption 808 parameters 809 @throws std::invalid_argument if encrypted is already in NTT form 810 @throws std::logic_error if result ciphertext is transparent 811 */ 812 void transform_to_ntt_inplace(Ciphertext &encrypted) const; 813 814 /** 815 Transforms a ciphertext to NTT domain. This functions applies David Harvey's Number Theoretic Transform 816 separately to each polynomial of a ciphertext. The result is stored in the destination_ntt parameter. 817 818 @param[in] encrypted The ciphertext to transform 819 @param[out] destination_ntt The ciphertext to overwrite with the transformed result 820 @throws std::invalid_argument if encrypted is not valid for the encryption 821 parameters 822 @throws std::invalid_argument if encrypted is already in NTT form 823 @throws std::logic_error if result ciphertext is transparent 824 */ transform_to_ntt(const Ciphertext & encrypted,Ciphertext & destination_ntt)825 inline void transform_to_ntt(const Ciphertext &encrypted, Ciphertext &destination_ntt) const 826 { 827 destination_ntt = encrypted; 828 transform_to_ntt_inplace(destination_ntt); 829 } 830 831 /** 832 Transforms a ciphertext back from NTT domain. This functions applies the inverse of David Harvey's Number 833 Theoretic Transform separately to each polynomial of a ciphertext. 834 835 @param[in] encrypted_ntt The ciphertext to transform 836 @throws std::invalid_argument if encrypted_ntt is not valid for the encryption 837 parameters 838 @throws std::invalid_argument if encrypted_ntt is not in NTT form 839 @throws std::logic_error if result ciphertext is transparent 840 */ 841 void transform_from_ntt_inplace(Ciphertext &encrypted_ntt) const; 842 843 /** 844 Transforms a ciphertext back from NTT domain. This functions applies the inverse of David Harvey's Number 845 Theoretic Transform separately to each polynomial of a ciphertext. The result is stored in the destination 846 parameter. 847 848 @param[in] encrypted_ntt The ciphertext to transform 849 @param[out] destination The ciphertext to overwrite with the transformed result 850 @throws std::invalid_argument if encrypted_ntt is not valid for the encryption 851 parameters 852 @throws std::invalid_argument if encrypted_ntt is not in NTT form 853 @throws std::logic_error if result ciphertext is transparent 854 */ transform_from_ntt(const Ciphertext & encrypted_ntt,Ciphertext & destination)855 inline void transform_from_ntt(const Ciphertext &encrypted_ntt, Ciphertext &destination) const 856 { 857 destination = encrypted_ntt; 858 transform_from_ntt_inplace(destination); 859 } 860 861 /** 862 Applies a Galois automorphism to a ciphertext. To evaluate the Galois automorphism, an appropriate set of Galois 863 keys must also be provided. Dynamic memory allocations in the process are allocated from the memory pool pointed 864 to by the given MemoryPoolHandle. 865 866 The desired Galois automorphism is given as a Galois element, and must be an odd integer in the interval 867 [1, M-1], where M = 2*N, and N = poly_modulus_degree. Used with batching, a Galois element 3^i % M corresponds 868 to a cyclic row rotation i steps to the left, and a Galois element 3^(N/2-i) % M corresponds to a cyclic row 869 rotation i steps to the right. The Galois element M-1 corresponds to a column rotation (row swap) in BFV, and 870 complex conjugation in CKKS. In the polynomial view (not batching), a Galois automorphism by a Galois element p 871 changes Enc(plain(x)) to Enc(plain(x^p)). 872 873 @param[in] encrypted The ciphertext to apply the Galois automorphism to 874 @param[in] galois_elt The Galois element 875 @param[in] galois_keys The Galois keys 876 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 877 @throws std::invalid_argument if encrypted or galois_keys is not valid for 878 the encryption parameters 879 @throws std::invalid_argument if galois_keys do not correspond to the top 880 level parameters in the current context 881 @throws std::invalid_argument if encrypted is not in the default NTT form 882 @throws std::invalid_argument if encrypted has size larger than 2 883 @throws std::invalid_argument if the Galois element is not valid 884 @throws std::invalid_argument if necessary Galois keys are not present 885 @throws std::invalid_argument if pool is uninitialized 886 @throws std::logic_error if keyswitching is not supported by the context 887 @throws std::logic_error if result ciphertext is transparent 888 */ 889 void apply_galois_inplace( 890 Ciphertext &encrypted, std::uint32_t galois_elt, const GaloisKeys &galois_keys, 891 MemoryPoolHandle pool = MemoryManager::GetPool()) const; 892 893 /** 894 Applies a Galois automorphism to a ciphertext and writes the result to the destination parameter. To evaluate 895 the Galois automorphism, an appropriate set of Galois keys must also be provided. Dynamic memory allocations in 896 the process are allocated from the memory pool pointed to by the given MemoryPoolHandle. 897 898 The desired Galois automorphism is given as a Galois element, and must be an odd integer in the interval 899 [1, M-1], where M = 2*N, and N = poly_modulus_degree. Used with batching, a Galois element 3^i % M corresponds 900 to a cyclic row rotation i steps to the left, and a Galois element 3^(N/2-i) % M corresponds to a cyclic row 901 rotation i steps to the right. The Galois element M-1 corresponds to a column rotation (row swap) in BFV, and 902 complex conjugation in CKKS. In the polynomial view (not batching), a Galois automorphism by a Galois element p 903 changes Enc(plain(x)) to Enc(plain(x^p)). 904 905 @param[in] encrypted The ciphertext to apply the Galois automorphism to 906 @param[in] galois_elt The Galois element 907 @param[in] galois_keys The Galois keys 908 @param[out] destination The ciphertext to overwrite with the result 909 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 910 @throws std::invalid_argument if encrypted or galois_keys is not valid for 911 the encryption parameters 912 @throws std::invalid_argument if galois_keys do not correspond to the top 913 level parameters in the current context 914 @throws std::invalid_argument if encrypted is not in the default NTT form 915 @throws std::invalid_argument if encrypted has size larger than 2 916 @throws std::invalid_argument if the Galois element is not valid 917 @throws std::invalid_argument if necessary Galois keys are not present 918 @throws std::invalid_argument if pool is uninitialized 919 @throws std::logic_error if keyswitching is not supported by the context 920 @throws std::logic_error if result ciphertext is transparent 921 */ 922 inline void apply_galois( 923 const Ciphertext &encrypted, std::uint32_t galois_elt, const GaloisKeys &galois_keys, 924 Ciphertext &destination, MemoryPoolHandle pool = MemoryManager::GetPool()) const 925 { 926 destination = encrypted; 927 apply_galois_inplace(destination, galois_elt, galois_keys, std::move(pool)); 928 } 929 930 /** 931 Rotates plaintext matrix rows cyclically. When batching is used with the BFV scheme, this function rotates the 932 encrypted plaintext matrix rows cyclically to the left (steps > 0) or to the right (steps < 0). Since the size 933 of the batched matrix is 2-by-(N/2), where N is the degree of the polynomial modulus, the number of steps to 934 rotate must have absolute value at most N/2-1. Dynamic memory allocations in the process are allocated from the 935 memory pool pointed to by the given MemoryPoolHandle. 936 937 @param[in] encrypted The ciphertext to rotate 938 @param[in] steps The number of steps to rotate (positive left, negative right) 939 @param[in] galois_keys The Galois keys 940 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 941 @throws std::logic_error if scheme is not scheme_type::bfv 942 @throws std::logic_error if the encryption parameters do not support batching 943 @throws std::invalid_argument if encrypted or galois_keys is not valid for 944 the encryption parameters 945 @throws std::invalid_argument if galois_keys do not correspond to the top 946 level parameters in the current context 947 @throws std::invalid_argument if encrypted is not in the default NTT form 948 @throws std::invalid_argument if encrypted has size larger than 2 949 @throws std::invalid_argument if steps has too big absolute value 950 @throws std::invalid_argument if necessary Galois keys are not present 951 @throws std::invalid_argument if pool is uninitialized 952 @throws std::logic_error if keyswitching is not supported by the context 953 @throws std::logic_error if result ciphertext is transparent 954 */ 955 inline void rotate_rows_inplace( 956 Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys, 957 MemoryPoolHandle pool = MemoryManager::GetPool()) const 958 { 959 if (context_.key_context_data()->parms().scheme() != scheme_type::bfv) 960 { 961 throw std::logic_error("unsupported scheme"); 962 } 963 rotate_internal(encrypted, steps, galois_keys, std::move(pool)); 964 } 965 966 /** 967 Rotates plaintext matrix rows cyclically. When batching is used with the BFV scheme, this function rotates the 968 encrypted plaintext matrix rows cyclically to the left (steps > 0) or to the right (steps < 0) and writes the 969 result to the destination parameter. Since the size of the batched matrix is 2-by-(N/2), where N is the degree 970 of the polynomial modulus, the number of steps to rotate must have absolute value at most N/2-1. Dynamic memory 971 allocations in the process are allocated from the memory pool pointed to by the given MemoryPoolHandle. 972 973 @param[in] encrypted The ciphertext to rotate 974 @param[in] steps The number of steps to rotate (positive left, negative right) 975 @param[in] galois_keys The Galois keys 976 @param[out] destination The ciphertext to overwrite with the rotated result 977 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 978 @throws std::logic_error if scheme is not scheme_type::bfv 979 @throws std::logic_error if the encryption parameters do not support batching 980 @throws std::invalid_argument if encrypted or galois_keys is not valid for 981 the encryption parameters 982 @throws std::invalid_argument if galois_keys do not correspond to the top 983 level parameters in the current context 984 @throws std::invalid_argument if encrypted is in NTT form 985 @throws std::invalid_argument if encrypted has size larger than 2 986 @throws std::invalid_argument if steps has too big absolute value 987 @throws std::invalid_argument if necessary Galois keys are not present 988 @throws std::invalid_argument if pool is uninitialized 989 @throws std::logic_error if keyswitching is not supported by the context 990 @throws std::logic_error if result ciphertext is transparent 991 */ 992 inline void rotate_rows( 993 const Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys, Ciphertext &destination, 994 MemoryPoolHandle pool = MemoryManager::GetPool()) const 995 { 996 destination = encrypted; 997 rotate_rows_inplace(destination, steps, galois_keys, std::move(pool)); 998 } 999 1000 /** 1001 Rotates plaintext matrix columns cyclically. When batching is used with the BFV scheme, this function rotates 1002 the encrypted plaintext matrix columns cyclically. Since the size of the batched matrix is 2-by-(N/2), where N 1003 is the degree of the polynomial modulus, this means simply swapping the two rows. Dynamic memory allocations in 1004 the process are allocated from the memory pool pointed to by the given MemoryPoolHandle. 1005 1006 @param[in] encrypted The ciphertext to rotate 1007 @param[in] galois_keys The Galois keys 1008 @param[out] destination The ciphertext to overwrite with the rotated result 1009 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 1010 @throws std::logic_error if scheme is not scheme_type::bfv 1011 @throws std::logic_error if the encryption parameters do not support batching 1012 @throws std::invalid_argument if encrypted or galois_keys is not valid for 1013 the encryption parameters 1014 @throws std::invalid_argument if galois_keys do not correspond to the top 1015 level parameters in the current context 1016 @throws std::invalid_argument if encrypted is in NTT form 1017 @throws std::invalid_argument if encrypted has size larger than 2 1018 @throws std::invalid_argument if necessary Galois keys are not present 1019 @throws std::invalid_argument if pool is uninitialized 1020 @throws std::logic_error if keyswitching is not supported by the context 1021 @throws std::logic_error if result ciphertext is transparent 1022 */ 1023 inline void rotate_columns_inplace( 1024 Ciphertext &encrypted, const GaloisKeys &galois_keys, 1025 MemoryPoolHandle pool = MemoryManager::GetPool()) const 1026 { 1027 if (context_.key_context_data()->parms().scheme() != scheme_type::bfv) 1028 { 1029 throw std::logic_error("unsupported scheme"); 1030 } 1031 conjugate_internal(encrypted, galois_keys, std::move(pool)); 1032 } 1033 1034 /** 1035 Rotates plaintext matrix columns cyclically. When batching is used with the BFV scheme, this function rotates 1036 the encrypted plaintext matrix columns cyclically, and writes the result to the destination parameter. Since the 1037 size of the batched matrix is 2-by-(N/2), where N is the degree of the polynomial modulus, this means simply 1038 swapping the two rows. Dynamic memory allocations in the process are allocated from the memory pool pointed to 1039 by the given MemoryPoolHandle. 1040 1041 @param[in] encrypted The ciphertext to rotate 1042 @param[in] galois_keys The Galois keys 1043 @param[out] destination The ciphertext to overwrite with the rotated result 1044 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 1045 @throws std::logic_error if scheme is not scheme_type::bfv 1046 @throws std::logic_error if the encryption parameters do not support batching 1047 @throws std::invalid_argument if encrypted or galois_keys is not valid for 1048 the encryption parameters 1049 @throws std::invalid_argument if galois_keys do not correspond to the top 1050 level parameters in the current context 1051 @throws std::invalid_argument if encrypted is in NTT form 1052 @throws std::invalid_argument if encrypted has size larger than 2 1053 @throws std::invalid_argument if necessary Galois keys are not present 1054 @throws std::invalid_argument if pool is uninitialized 1055 @throws std::logic_error if keyswitching is not supported by the context 1056 @throws std::logic_error if result ciphertext is transparent 1057 */ 1058 inline void rotate_columns( 1059 const Ciphertext &encrypted, const GaloisKeys &galois_keys, Ciphertext &destination, 1060 MemoryPoolHandle pool = MemoryManager::GetPool()) const 1061 { 1062 destination = encrypted; 1063 rotate_columns_inplace(destination, galois_keys, std::move(pool)); 1064 } 1065 1066 /** 1067 Rotates plaintext vector cyclically. When using the CKKS scheme, this function rotates the encrypted plaintext 1068 vector cyclically to the left (steps > 0) or to the right (steps < 0). Since the size of the batched matrix is 1069 2-by-(N/2), where N is the degree of the polynomial modulus, the number of steps to rotate must have absolute 1070 value at most N/2-1. Dynamic memory allocations in the process are allocated from the memory pool pointed to by 1071 the given MemoryPoolHandle. 1072 1073 @param[in] encrypted The ciphertext to rotate 1074 @param[in] steps The number of steps to rotate (positive left, negative right) 1075 @param[in] galois_keys The Galois keys 1076 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 1077 @throws std::logic_error if scheme is not scheme_type::ckks 1078 @throws std::invalid_argument if encrypted or galois_keys is not valid for 1079 the encryption parameters 1080 @throws std::invalid_argument if galois_keys do not correspond to the top 1081 level parameters in the current context 1082 @throws std::invalid_argument if encrypted is not in the default NTT form 1083 @throws std::invalid_argument if encrypted has size larger than 2 1084 @throws std::invalid_argument if steps has too big absolute value 1085 @throws std::invalid_argument if necessary Galois keys are not present 1086 @throws std::invalid_argument if pool is uninitialized 1087 @throws std::logic_error if keyswitching is not supported by the context 1088 @throws std::logic_error if result ciphertext is transparent 1089 */ 1090 inline void rotate_vector_inplace( 1091 Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys, 1092 MemoryPoolHandle pool = MemoryManager::GetPool()) const 1093 { 1094 if (context_.key_context_data()->parms().scheme() != scheme_type::ckks) 1095 { 1096 throw std::logic_error("unsupported scheme"); 1097 } 1098 rotate_internal(encrypted, steps, galois_keys, std::move(pool)); 1099 } 1100 1101 /** 1102 Rotates plaintext vector cyclically. When using the CKKS scheme, this function rotates the encrypted plaintext 1103 vector cyclically to the left (steps > 0) or to the right (steps < 0) and writes the result to the destination 1104 parameter. Since the size of the batched matrix is 2-by-(N/2), where N is the degree of the polynomial modulus, 1105 the number of steps to rotate must have absolute value at most N/2-1. Dynamic memory allocations in the process 1106 are allocated from the memory pool pointed to by the given MemoryPoolHandle. 1107 1108 @param[in] encrypted The ciphertext to rotate 1109 @param[in] steps The number of steps to rotate (positive left, negative right) 1110 @param[in] galois_keys The Galois keys 1111 @param[out] destination The ciphertext to overwrite with the rotated result 1112 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 1113 @throws std::logic_error if scheme is not scheme_type::ckks 1114 @throws std::invalid_argument if encrypted or galois_keys is not valid for 1115 the encryption parameters 1116 @throws std::invalid_argument if galois_keys do not correspond to the top 1117 level parameters in the current context 1118 @throws std::invalid_argument if encrypted is in NTT form 1119 @throws std::invalid_argument if encrypted has size larger than 2 1120 @throws std::invalid_argument if steps has too big absolute value 1121 @throws std::invalid_argument if necessary Galois keys are not present 1122 @throws std::invalid_argument if pool is uninitialized 1123 @throws std::logic_error if keyswitching is not supported by the context 1124 @throws std::logic_error if result ciphertext is transparent 1125 */ 1126 inline void rotate_vector( 1127 const Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys, Ciphertext &destination, 1128 MemoryPoolHandle pool = MemoryManager::GetPool()) const 1129 { 1130 destination = encrypted; 1131 rotate_vector_inplace(destination, steps, galois_keys, std::move(pool)); 1132 } 1133 1134 /** 1135 Complex conjugates plaintext slot values. When using the CKKS scheme, this function complex conjugates all 1136 values in the underlying plaintext. Dynamic memory allocations in the process are allocated from the memory pool 1137 pointed to by the given MemoryPoolHandle. 1138 1139 @param[in] encrypted The ciphertext to rotate 1140 @param[in] galois_keys The Galois keys 1141 @param[out] destination The ciphertext to overwrite with the rotated result 1142 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 1143 @throws std::logic_error if scheme is not scheme_type::ckks 1144 @throws std::invalid_argument if encrypted or galois_keys is not valid for 1145 the encryption parameters 1146 @throws std::invalid_argument if galois_keys do not correspond to the top 1147 level parameters in the current context 1148 @throws std::invalid_argument if encrypted is in NTT form 1149 @throws std::invalid_argument if encrypted has size larger than 2 1150 @throws std::invalid_argument if necessary Galois keys are not present 1151 @throws std::invalid_argument if pool is uninitialized 1152 @throws std::logic_error if keyswitching is not supported by the context 1153 @throws std::logic_error if result ciphertext is transparent 1154 */ 1155 inline void complex_conjugate_inplace( 1156 Ciphertext &encrypted, const GaloisKeys &galois_keys, 1157 MemoryPoolHandle pool = MemoryManager::GetPool()) const 1158 { 1159 if (context_.key_context_data()->parms().scheme() != scheme_type::ckks) 1160 { 1161 throw std::logic_error("unsupported scheme"); 1162 } 1163 conjugate_internal(encrypted, galois_keys, std::move(pool)); 1164 } 1165 1166 /** 1167 Complex conjugates plaintext slot values. When using the CKKS scheme, this function complex conjugates all 1168 values in the underlying plaintext, and writes the result to the destination parameter. Dynamic memory 1169 allocations in the process are allocated from the memory pool pointed to by the given MemoryPoolHandle. 1170 1171 @param[in] encrypted The ciphertext to rotate 1172 @param[in] galois_keys The Galois keys 1173 @param[out] destination The ciphertext to overwrite with the rotated result 1174 @param[in] pool The MemoryPoolHandle pointing to a valid memory pool 1175 @throws std::logic_error if scheme is not scheme_type::ckks 1176 @throws std::invalid_argument if encrypted or galois_keys is not valid for 1177 the encryption parameters 1178 @throws std::invalid_argument if galois_keys do not correspond to the top 1179 level parameters in the current context 1180 @throws std::invalid_argument if encrypted is in NTT form 1181 @throws std::invalid_argument if encrypted has size larger than 2 1182 @throws std::invalid_argument if necessary Galois keys are not present 1183 @throws std::invalid_argument if pool is uninitialized 1184 @throws std::logic_error if keyswitching is not supported by the context 1185 @throws std::logic_error if result ciphertext is transparent 1186 */ 1187 inline void complex_conjugate( 1188 const Ciphertext &encrypted, const GaloisKeys &galois_keys, Ciphertext &destination, 1189 MemoryPoolHandle pool = MemoryManager::GetPool()) const 1190 { 1191 destination = encrypted; 1192 complex_conjugate_inplace(destination, galois_keys, std::move(pool)); 1193 } 1194 1195 /** 1196 Enables access to private members of seal::Evaluator for SEAL_C. 1197 */ 1198 struct EvaluatorPrivateHelper; 1199 1200 private: 1201 Evaluator(const Evaluator ©) = delete; 1202 1203 Evaluator(Evaluator &&source) = delete; 1204 1205 Evaluator &operator=(const Evaluator &assign) = delete; 1206 1207 Evaluator &operator=(Evaluator &&assign) = delete; 1208 1209 void bfv_multiply(Ciphertext &encrypted1, const Ciphertext &encrypted2, MemoryPoolHandle pool) const; 1210 1211 void ckks_multiply(Ciphertext &encrypted1, const Ciphertext &encrypted2, MemoryPoolHandle pool) const; 1212 1213 void bfv_square(Ciphertext &encrypted, MemoryPoolHandle pool) const; 1214 1215 void ckks_square(Ciphertext &encrypted, MemoryPoolHandle pool) const; 1216 1217 void relinearize_internal( 1218 Ciphertext &encrypted, const RelinKeys &relin_keys, std::size_t destination_size, 1219 MemoryPoolHandle pool) const; 1220 1221 void mod_switch_scale_to_next( 1222 const Ciphertext &encrypted, Ciphertext &destination, MemoryPoolHandle pool) const; 1223 1224 void mod_switch_drop_to_next(const Ciphertext &encrypted, Ciphertext &destination, MemoryPoolHandle pool) const; 1225 1226 void mod_switch_drop_to_next(Plaintext &plain) const; 1227 1228 void rotate_internal( 1229 Ciphertext &encrypted, int steps, const GaloisKeys &galois_keys, MemoryPoolHandle pool) const; 1230 conjugate_internal(Ciphertext & encrypted,const GaloisKeys & galois_keys,MemoryPoolHandle pool)1231 inline void conjugate_internal( 1232 Ciphertext &encrypted, const GaloisKeys &galois_keys, MemoryPoolHandle pool) const 1233 { 1234 // Verify parameters. 1235 auto context_data_ptr = context_.get_context_data(encrypted.parms_id()); 1236 if (!context_data_ptr) 1237 { 1238 throw std::invalid_argument("encrypted is not valid for encryption parameters"); 1239 } 1240 1241 // Extract encryption parameters. 1242 auto &context_data = *context_data_ptr; 1243 if (!context_data.qualifiers().using_batching) 1244 { 1245 throw std::logic_error("encryption parameters do not support batching"); 1246 } 1247 1248 auto galois_tool = context_data.galois_tool(); 1249 1250 // Perform rotation and key switching 1251 apply_galois_inplace(encrypted, galois_tool->get_elt_from_step(0), galois_keys, std::move(pool)); 1252 } 1253 1254 void switch_key_inplace( 1255 Ciphertext &encrypted, util::ConstRNSIter target_iter, const KSwitchKeys &kswitch_keys, 1256 std::size_t key_index, MemoryPoolHandle pool = MemoryManager::GetPool()) const; 1257 1258 void multiply_plain_normal(Ciphertext &encrypted, const Plaintext &plain, MemoryPoolHandle pool) const; 1259 1260 void multiply_plain_ntt(Ciphertext &encrypted_ntt, const Plaintext &plain_ntt) const; 1261 1262 SEALContext context_; 1263 }; 1264 } // namespace seal 1265