1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#pragma once
#include "seal/encryptionparams.h"
#include "seal/memorymanager.h"
#include "seal/modulus.h"
#include "seal/util/galois.h"
#include "seal/util/ntt.h"
#include "seal/util/pointer.h"
#include "seal/util/rns.h"
#include <memory>
#include <unordered_map>
namespace seal
{
/**
Stores a set of attributes (qualifiers) of a set of encryption parameters.
These parameters are mainly used internally in various parts of the library,
e.g., to determine which algorithmic optimizations the current support. The
qualifiers are automatically created by the SEALContext class, silently passed
on to classes such as Encryptor, Evaluator, and Decryptor, and the only way to
change them is by changing the encryption parameters themselves. In other
words, a user will never have to create their own instance of this class, and
in most cases never have to worry about it at all.
*/
class EncryptionParameterQualifiers
{
public:
/**
Identifies the reason why encryption parameters are not valid.
*/
enum class error_type : int
{
/**
constructed but not yet validated
*/
none = -1,
/**
valid
*/
success = 0,
/**
scheme must be BFV or CKKS or BGV
*/
invalid_scheme = 1,
/**
coeff_modulus's primes' count is not bounded by SEAL_COEFF_MOD_COUNT_MIN(MAX)
*/
invalid_coeff_modulus_size = 2,
/**
coeff_modulus's primes' bit counts are not bounded by SEAL_USER_MOD_BIT_COUNT_MIN(MAX)
*/
invalid_coeff_modulus_bit_count = 3,
/**
coeff_modulus's primes are not congruent to 1 modulo (2 * poly_modulus_degree)
*/
invalid_coeff_modulus_no_ntt = 4,
/**
poly_modulus_degree is not bounded by SEAL_POLY_MOD_DEGREE_MIN(MAX)
*/
invalid_poly_modulus_degree = 5,
/**
poly_modulus_degree is not a power of two
*/
invalid_poly_modulus_degree_non_power_of_two = 6,
/**
parameters are too large to fit in size_t type
*/
invalid_parameters_too_large = 7,
/**
parameters are not compliant with HomomorphicEncryption.org security standard
*/
invalid_parameters_insecure = 8,
/**
RNSBase cannot be constructed
*/
failed_creating_rns_base = 9,
/**
plain_modulus's bit count is not bounded by SEAL_PLAIN_MOD_BIT_COUNT_MIN(MAX)
*/
invalid_plain_modulus_bit_count = 10,
/**
plain_modulus is not coprime to coeff_modulus
*/
invalid_plain_modulus_coprimality = 11,
/**
plain_modulus is not smaller than coeff_modulus
*/
invalid_plain_modulus_too_large = 12,
/**
plain_modulus is not zero
*/
invalid_plain_modulus_nonzero = 13,
/**
RNSTool cannot be constructed
*/
failed_creating_rns_tool = 14,
};
/**
The variable parameter_error is set to:
- none, if parameters are not validated;
- success, if parameters are considered valid by Microsoft SEAL;
- other values, if parameters are validated and invalid.
*/
error_type parameter_error;
/**
Returns the name of parameter_error.
*/
SEAL_NODISCARD const char *parameter_error_name() const noexcept;
/**
Returns a comprehensive message that interprets parameter_error.
*/
SEAL_NODISCARD const char *parameter_error_message() const noexcept;
/**
Tells whether parameter_error is error_type::success.
*/
SEAL_NODISCARD inline bool parameters_set() const noexcept
{
return parameter_error == error_type::success;
}
/**
Tells whether FFT can be used for polynomial multiplication. If the
polynomial modulus is of the form X^N+1, where N is a power of two, then
FFT can be used for fast multiplication of polynomials modulo the polynomial
modulus. In this case the variable using_fft will be set to true. However,
currently Microsoft SEAL requires this to be the case for the parameters
to be valid. Therefore, parameters_set can only be true if using_fft is
true.
*/
bool using_fft;
/**
Tells whether NTT can be used for polynomial multiplication. If the primes
in the coefficient modulus are congruent to 1 modulo 2N, where X^N+1 is the
polynomial modulus and N is a power of two, then the number-theoretic
transform (NTT) can be used for fast multiplications of polynomials modulo
the polynomial modulus and coefficient modulus. In this case the variable
using_ntt will be set to true. However, currently Microsoft SEAL requires
this to be the case for the parameters to be valid. Therefore, parameters_set
can only be true if using_ntt is true.
*/
bool using_ntt;
/**
Tells whether batching is supported by the encryption parameters. If the
plaintext modulus is congruent to 1 modulo 2N, where X^N+1 is the polynomial
modulus and N is a power of two, then it is possible to use the BatchEncoder
class to view plaintext elements as 2-by-(N/2) matrices of integers modulo
the plaintext modulus. This is called batching, and allows the user to
operate on the matrix elements (slots) in a SIMD fashion, and rotate the
matrix rows and columns. When the computation is easily vectorizable, using
batching can yield a huge performance boost. If the encryption parameters
support batching, the variable using_batching is set to true.
*/
bool using_batching;
/**
Tells whether fast plain lift is supported by the encryption parameters.
A certain performance optimization in multiplication of a ciphertext by
a plaintext (Evaluator::multiply_plain) and in transforming a plaintext
element to NTT domain (Evaluator::transform_to_ntt) can be used when the
plaintext modulus is smaller than each prime in the coefficient modulus.
In this case the variable using_fast_plain_lift is set to true.
*/
bool using_fast_plain_lift;
/**
Tells whether the coefficient modulus consists of a set of primes that
are in decreasing order. If this is true, certain modular reductions in
base conversion can be omitted, improving performance.
*/
bool using_descending_modulus_chain;
/**
Tells whether the encryption parameters are secure based on the standard
parameters from HomomorphicEncryption.org security standard.
*/
sec_level_type sec_level;
private:
EncryptionParameterQualifiers()
: parameter_error(error_type::none), using_fft(false), using_ntt(false), using_batching(false),
using_fast_plain_lift(false), using_descending_modulus_chain(false), sec_level(sec_level_type::none)
{}
friend class SEALContext;
};
/**
Performs sanity checks (validation) and pre-computations for a given set of encryption
parameters. While the EncryptionParameters class is intended to be a light-weight class
to store the encryption parameters, the SEALContext class is a heavy-weight class that
is constructed from a given set of encryption parameters. It validates the parameters
for correctness, evaluates their properties, and performs and stores the results of
several costly pre-computations.
After the user has set at least the poly_modulus, coeff_modulus, and plain_modulus
parameters in a given EncryptionParameters instance, the parameters can be validated
for correctness and functionality by constructing an instance of SEALContext. The
constructor of SEALContext does all of its work automatically, and concludes by
constructing and storing an instance of the EncryptionParameterQualifiers class, with
its flags set according to the properties of the given parameters. If the created
instance of EncryptionParameterQualifiers has the parameters_set flag set to true, the
given parameter set has been deemed valid and is ready to be used. If the parameters
were for some reason not appropriately set, the parameters_set flag will be false,
and a new SEALContext will have to be created after the parameters are corrected.
By default, SEALContext creates a chain of SEALContext::ContextData instances. The
first one in the chain corresponds to special encryption parameters that are reserved
to be used by the various key classes (SecretKey, PublicKey, etc.). These are the exact
same encryption parameters that are created by the user and passed to th constructor of
SEALContext. The functions key_context_data() and key_parms_id() return the ContextData
and the parms_id corresponding to these special parameters. The rest of the ContextData
instances in the chain correspond to encryption parameters that are derived from the
first encryption parameters by always removing the last one of the moduli in the
coeff_modulus, until the resulting parameters are no longer valid, e.g., there are no
more primes left. These derived encryption parameters are used by ciphertexts and
plaintexts and their respective ContextData can be accessed through the
get_context_data(parms_id_type) function. The functions first_context_data() and
last_context_data() return the ContextData corresponding to the first and the last
set of parameters in the "data" part of the chain, i.e., the second and the last element
in the full chain. The chain itself is a doubly linked list, and is referred to as the
modulus switching chain.
@see EncryptionParameters for more details on the parameters.
@see EncryptionParameterQualifiers for more details on the qualifiers.
*/
class SEALContext
{
public:
/**
Class to hold pre-computation data for a given set of encryption parameters.
*/
class ContextData
{
friend class SEALContext;
public:
ContextData() = delete;
ContextData(const ContextData ©) = delete;
ContextData(ContextData &&move) = default;
ContextData &operator=(ContextData &&move) = default;
/**
Returns a const reference to the underlying encryption parameters.
*/
SEAL_NODISCARD inline const EncryptionParameters &parms() const noexcept
{
return parms_;
}
/**
Returns the parms_id of the current parameters.
*/
SEAL_NODISCARD inline const parms_id_type &parms_id() const noexcept
{
return parms_.parms_id();
}
/**
Returns a copy of EncryptionParameterQualifiers corresponding to the
current encryption parameters. Note that to change the qualifiers it is
necessary to create a new instance of SEALContext once appropriate changes
to the encryption parameters have been made.
*/
SEAL_NODISCARD inline EncryptionParameterQualifiers qualifiers() const noexcept
{
return qualifiers_;
}
/**
Returns a pointer to a pre-computed product of all primes in the coefficient
modulus. The security of the encryption parameters largely depends on the
bit-length of this product, and on the degree of the polynomial modulus.
*/
SEAL_NODISCARD inline const std::uint64_t *total_coeff_modulus() const noexcept
{
return total_coeff_modulus_.get();
}
/**
Returns the significant bit count of the total coefficient modulus.
*/
SEAL_NODISCARD inline int total_coeff_modulus_bit_count() const noexcept
{
return total_coeff_modulus_bit_count_;
}
/**
Returns a constant pointer to the RNSTool.
*/
SEAL_NODISCARD inline const util::RNSTool *rns_tool() const noexcept
{
return rns_tool_.get();
}
/**
Returns a constant pointer to the NTT tables.
*/
SEAL_NODISCARD inline const util::NTTTables *small_ntt_tables() const noexcept
{
return small_ntt_tables_.get();
}
/**
Returns a constant pointer to the NTT tables.
*/
SEAL_NODISCARD inline const util::NTTTables *plain_ntt_tables() const noexcept
{
return plain_ntt_tables_.get();
}
/**
Returns a constant pointer to the GaloisTool.
*/
SEAL_NODISCARD inline const util::GaloisTool *galois_tool() const noexcept
{
return galois_tool_.get();
}
/**
Return a pointer to BFV "Delta", i.e. coefficient modulus divided by
plaintext modulus.
*/
SEAL_NODISCARD inline const util::MultiplyUIntModOperand *coeff_div_plain_modulus() const noexcept
{
return coeff_div_plain_modulus_.get();
}
/**
Return the threshold for the upper half of integers modulo plain_modulus.
This is simply (plain_modulus + 1) / 2.
*/
SEAL_NODISCARD inline std::uint64_t plain_upper_half_threshold() const noexcept
{
return plain_upper_half_threshold_;
}
/**
Return a pointer to the plaintext upper half increment, i.e. coeff_modulus
minus plain_modulus. The upper half increment is represented as an integer
for the full product coeff_modulus if using_fast_plain_lift is false and is
otherwise represented modulo each of the coeff_modulus primes in order.
*/
SEAL_NODISCARD inline const std::uint64_t *plain_upper_half_increment() const noexcept
{
return plain_upper_half_increment_.get();
}
/**
Return a pointer to the upper half threshold with respect to the total
coefficient modulus. This is needed in CKKS decryption.
*/
SEAL_NODISCARD inline const std::uint64_t *upper_half_threshold() const noexcept
{
return upper_half_threshold_.get();
}
/**
Return a pointer to the upper half increment used for computing Delta*m
and converting the coefficients to modulo coeff_modulus. For example,
t-1 in plaintext should change into
q - Delta = Delta*t + r_t(q) - Delta
= Delta*(t-1) + r_t(q)
so multiplying the message by Delta is not enough and requires also an
addition of r_t(q). This is precisely the upper_half_increment. Note that
this operation is only done for negative message coefficients, i.e. those
that exceed plain_upper_half_threshold.
*/
SEAL_NODISCARD inline const std::uint64_t *upper_half_increment() const noexcept
{
return upper_half_increment_.get();
}
/**
Return the non-RNS form of upper_half_increment which is q mod t.
*/
SEAL_NODISCARD inline std::uint64_t coeff_modulus_mod_plain_modulus() const noexcept
{
return coeff_modulus_mod_plain_modulus_;
}
/**
Returns a shared_ptr to the context data corresponding to the previous parameters
in the modulus switching chain. If the current data is the first one in the
chain, then the result is nullptr.
*/
SEAL_NODISCARD inline std::shared_ptr<const ContextData> prev_context_data() const noexcept
{
return prev_context_data_.lock();
}
/**
Returns a shared_ptr to the context data corresponding to the next parameters
in the modulus switching chain. If the current data is the last one in the
chain, then the result is nullptr.
*/
SEAL_NODISCARD inline std::shared_ptr<const ContextData> next_context_data() const noexcept
{
return next_context_data_;
}
/**
Returns the index of the parameter set in a chain. The initial parameters
have index 0 and the index increases sequentially in the parameter chain.
*/
SEAL_NODISCARD inline std::size_t chain_index() const noexcept
{
return chain_index_;
}
private:
ContextData(EncryptionParameters parms, MemoryPoolHandle pool) : pool_(std::move(pool)), parms_(parms)
{
if (!pool_)
{
throw std::invalid_argument("pool is uninitialized");
}
}
MemoryPoolHandle pool_;
EncryptionParameters parms_;
EncryptionParameterQualifiers qualifiers_;
util::Pointer<util::RNSTool> rns_tool_;
util::Pointer<util::NTTTables> small_ntt_tables_;
util::Pointer<util::NTTTables> plain_ntt_tables_;
util::Pointer<util::GaloisTool> galois_tool_;
util::Pointer<std::uint64_t> total_coeff_modulus_;
int total_coeff_modulus_bit_count_ = 0;
util::Pointer<util::MultiplyUIntModOperand> coeff_div_plain_modulus_;
std::uint64_t plain_upper_half_threshold_ = 0;
util::Pointer<std::uint64_t> plain_upper_half_increment_;
util::Pointer<std::uint64_t> upper_half_threshold_;
util::Pointer<std::uint64_t> upper_half_increment_;
std::uint64_t coeff_modulus_mod_plain_modulus_ = 0;
std::weak_ptr<const ContextData> prev_context_data_;
std::shared_ptr<const ContextData> next_context_data_{ nullptr };
std::size_t chain_index_ = 0;
};
/**
Creates an instance of SEALContext and performs several pre-computations
on the given EncryptionParameters.
@param[in] parms The encryption parameters
@param[in] expand_mod_chain Determines whether the modulus switching chain
should be created
@param[in] sec_level Determines whether a specific security level should be
enforced according to HomomorphicEncryption.org security standard
*/
SEALContext(
const EncryptionParameters &parms, bool expand_mod_chain = true,
sec_level_type sec_level = sec_level_type::tc128)
: SEALContext(parms, expand_mod_chain, sec_level, MemoryManager::GetPool())
{}
/**
Creates a new SEALContext by copying a given one.
@param[in] copy The SEALContext to copy from
*/
SEALContext(const SEALContext ©) = default;
/**
Creates a new SEALContext by moving a given one.
@param[in] source The SEALContext to move from
*/
SEALContext(SEALContext &&source) = default;
/**
Copies a given SEALContext to the current one.
@param[in] assign The SEALContext to copy from
*/
SEALContext &operator=(const SEALContext &assign) = default;
/**
Moves a given SEALContext to the current one.
@param[in] assign The SEALContext to move from
*/
SEALContext &operator=(SEALContext &&assign) = default;
/**
Returns the ContextData corresponding to encryption parameters with a given
parms_id. If parameters with the given parms_id are not found then the
function returns nullptr.
@param[in] parms_id The parms_id of the encryption parameters
*/
SEAL_NODISCARD inline std::shared_ptr<const ContextData> get_context_data(parms_id_type parms_id) const
{
auto data = context_data_map_.find(parms_id);
return (data != context_data_map_.end()) ? data->second : std::shared_ptr<ContextData>{ nullptr };
}
/**
Returns the ContextData corresponding to encryption parameters that are
used for keys.
*/
SEAL_NODISCARD inline std::shared_ptr<const ContextData> key_context_data() const
{
auto data = context_data_map_.find(key_parms_id_);
return (data != context_data_map_.end()) ? data->second : std::shared_ptr<ContextData>{ nullptr };
}
/**
Returns the ContextData corresponding to the first encryption parameters
that are used for data.
*/
SEAL_NODISCARD inline std::shared_ptr<const ContextData> first_context_data() const
{
auto data = context_data_map_.find(first_parms_id_);
return (data != context_data_map_.end()) ? data->second : std::shared_ptr<ContextData>{ nullptr };
}
/**
Returns the ContextData corresponding to the last encryption parameters
that are used for data.
*/
SEAL_NODISCARD inline std::shared_ptr<const ContextData> last_context_data() const
{
auto data = context_data_map_.find(last_parms_id_);
return (data != context_data_map_.end()) ? data->second : std::shared_ptr<ContextData>{ nullptr };
}
/**
Returns whether the first_context_data's encryption parameters are valid.
*/
SEAL_NODISCARD inline bool parameters_set() const
{
return first_context_data() ? first_context_data()->qualifiers_.parameters_set() : false;
}
/**
Returns the name of encryption parameters' error.
*/
SEAL_NODISCARD inline const char *parameter_error_name() const
{
return first_context_data() ? first_context_data()->qualifiers_.parameter_error_name()
: "SEALContext is empty";
}
/**
Returns a comprehensive message that interprets encryption parameters' error.
*/
SEAL_NODISCARD inline const char *parameter_error_message() const
{
return first_context_data() ? first_context_data()->qualifiers_.parameter_error_message()
: "SEALContext is empty";
}
/**
Returns a parms_id_type corresponding to the set of encryption parameters
that are used for keys.
*/
SEAL_NODISCARD inline const parms_id_type &key_parms_id() const noexcept
{
return key_parms_id_;
}
/**
Returns a parms_id_type corresponding to the first encryption parameters
that are used for data.
*/
SEAL_NODISCARD inline const parms_id_type &first_parms_id() const noexcept
{
return first_parms_id_;
}
/**
Returns a parms_id_type corresponding to the last encryption parameters
that are used for data.
*/
SEAL_NODISCARD inline const parms_id_type &last_parms_id() const noexcept
{
return last_parms_id_;
}
/**
Returns whether the coefficient modulus supports keyswitching. In practice,
support for keyswitching is required by Evaluator::relinearize,
Evaluator::apply_galois, and all rotation and conjugation operations. For
keyswitching to be available, the coefficient modulus parameter must consist
of at least two prime number factors.
*/
SEAL_NODISCARD inline bool using_keyswitching() const noexcept
{
return using_keyswitching_;
}
private:
/**
Creates an instance of SEALContext, and performs several pre-computations
on the given EncryptionParameters.
@param[in] parms The encryption parameters
@param[in] expand_mod_chain Determines whether the modulus switching chain
should be created
@param[in] sec_level Determines whether a specific security level should be
enforced according to HomomorphicEncryption.org security standard
@param[in] pool The MemoryPoolHandle pointing to a valid memory pool
@throws std::invalid_argument if pool is uninitialized
*/
SEALContext(EncryptionParameters parms, bool expand_mod_chain, sec_level_type sec_level, MemoryPoolHandle pool);
ContextData validate(EncryptionParameters parms);
/**
Create the next context_data by dropping the last element from coeff_modulus.
If the new encryption parameters are not valid, returns parms_id_zero.
Otherwise, returns the parms_id of the next parameter and appends the next
context_data to the chain.
*/
parms_id_type create_next_context_data(const parms_id_type &prev_parms);
MemoryPoolHandle pool_;
parms_id_type key_parms_id_;
parms_id_type first_parms_id_;
parms_id_type last_parms_id_;
std::unordered_map<parms_id_type, std::shared_ptr<const ContextData>> context_data_map_{};
/**
Is HomomorphicEncryption.org security standard enforced?
*/
sec_level_type sec_level_;
/**
Is keyswitching supported by the encryption parameters?
*/
bool using_keyswitching_;
};
} // namespace seal