SEAL中CRT 编码相关实现及说明
终于还是要读源码了,终究摆脱不了数学的噩梦,先从读懂他们的代码开始。
文件原位置SEAL/Polycrt.h
介绍
提供CRT批处理功能。 如果多项式模数是 x N + 1 x^N+1 xN+1,并且明文模数是素数 T T T 使得 T ≡ 1 m o d    2 N T \equiv 1 \mod 2N T≡1mod2N ,则PolyCRTBuilder允许SEAL明文元素被视为 2 × ( N / 2 ) 2 \times (N/2) 2×(N/2) 的矩阵,在这种加密矩阵上执行的同态操作是应用系数(槽),为可矢量化的计算启用强大的SIMD功能。 此功能通常在同态加密文献中称为“批处理”。
-
par数学背景
从数学上讲,如果poly_modulus是 x N + 1 x^N+1 xN+1, N N N 是 2 2 2 的幂,plain_modulus是素数 T T T,使得 2 N 2N 2N 整除 T − 1 T-1 T−1,则模 T T T的整数包含 一个 2 N 2N 2N 的原始根,多项式 X N + 1 X ^ N + 1 XN+1分裂成 n n n个不同的线性因子,如 X N + 1 = ( X − a 1 ) ∗ ⋯ ∗ ( X − a N ) m o d    T X ^ N + 1 =(X-a_1)*\dots *(X-a_N)\mod T XN+1=(X−a1)∗⋯∗(X−aN)modT,其中常数 a 1 , … , a n a_1,\dots ,a_n a1,…,an 都是不同的原始的第 2 N 2N 2N个整数的根在整数模 T T T中。中国剩余定理(CRT)表明在这种情况下明文空间 Z T [ X ] / ( X N + 1 ) Z_T [X] /(X ^ N + 1) ZT[X]/(XN+1)与 N N N同构(作为代数)折叠领域的直接产品 Z T Z_T ZT。同构很容易在两个方向上明确计算,这就是这个类的作用。此外,扩张的伽罗瓦群是 ( Z / 2 N Z ) ∗ 〜 = Z / 2 Z × Z / ( N / 2 ) (Z / 2NZ)*〜= Z / 2Z×Z /(N / 2) (Z/2NZ)∗〜=Z/2Z×Z/(N/2),其对原始根的作用很容易描述。由于批处理时隙与统一的原始根对应1对1,因此通过置换时隙在明文上应用Galois自同构。通过应用伽罗瓦群的两个循环子群的生成器,我们可以有效地将明文视为2乘 N / 2 N / 2 N/2 矩阵,并且能够实现循环行旋转和列旋转(行交换)。 -
par有效参数
是否可以使用批处理取决于是否适当选择了明文模数。 因此,要构造PolyCRTBuilder,用户必须提供SEALContext的实例,以使其关联的EncryptionParameterQualifiers对象将flags_set和enable_batching设置为true。 -
par重载
对于分解函数,我们提供了两个有关操作期间所需分配中使用的内存池的重载。 在一次重载中,PolyCRTBuilder的本地内存池(用于存储预计算结果和其他成员变量)用于此目的,而在另一个重载中,用户可以提供要使用的MemoryPoolHandle。 这是为了允许多个线程同时使用一个PolyCRTBuilder,而不会在操作期间发生的分配中遇到线程争用。 例如,可以跨任意数量的线程共享一个PolyCRTBuilder,但是在每个线程中通过为其提供一个线程本地MemoryPoolHandle来调用加密函数。 对于开发人员而言,了解其工作原理以避免不必要的性能瓶颈非常重要。@有关加密参数的详细信息,请参阅EncryptionParameters。
@see EncryptionParameterQualifiers了解有关参数限定符的更多信息。
@see Evaluator用于旋转加密矩阵的行和列。
overview
构造函数
基本构造
PolyCRTBuilder(const SEALContext &context, const MemoryPoolHandle &pool = MemoryPoolHandle::Global());
创建PolyCRTBuilder。 通过SEALContext对象提供的加密参数必须支持批处理。 动态分配的成员变量是从给定的MemoryPoolHandle指向的内存池中分配的。 默认情况下,使用全局内存池。
@param [in] context SEALContext
@param [in] pool MemoryPoolHandle 指向有效的内存池
如果加密参数对批处理无效,则@throws std :: invalid_argument
如果池未初始化,则@throws std :: invalid_argument
PolyCRTBuilder::PolyCRTBuilder(const SEALContext &context, const MemoryPoolHandle &pool) :
pool_(pool), parms_(context.parms()),
ntt_tables_(pool_),
slots_(parms_.poly_modulus().coeff_count() - 1),
qualifiers_(context.qualifiers())
{
int coeff_count = parms_.poly_modulus().coeff_count();
// Verify parameters
if (!qualifiers_.parameters_set)
{
throw invalid_argument("encryption parameters are not set correctly");
}
if (!qualifiers_.enable_batching)
{
throw invalid_argument("encryption parameters are not valid for batching");
}
if (!pool)
{
throw invalid_argument("pool is uninitialized");
}
// Set mod_ and polymod_
mod_ = parms_.plain_modulus();
polymod_ = PolyModulus(parms_.poly_modulus().pointer(), coeff_count,
parms_.poly_modulus().coeff_uint64_count());
// Reserve space for all of the primitive roots
roots_of_unity_ = allocate_uint(slots_, pool_);
// Copy over NTT tables (switching to local pool)
ntt_tables_ = context.plain_ntt_tables_;
// Fill the vector of roots of unity with all distinct odd powers of generator.
// These are all the primitive (2*slots_)-th roots of unity in integers modulo parms_.plain_modulus().
populate_roots_of_unity_vector();
// Populate matrix representation index map
populate_matrix_reps_index_map();
}
深拷贝
PolyCRTBuilder(const PolyCRTBuilder ©);
创建给定PolyCRTBuilder的深拷贝。
@param [in] copy 要复制的PolyCRTBuilder
PolyCRTBuilder::PolyCRTBuilder(const PolyCRTBuilder ©) :
pool_(copy.pool_), parms_(copy.parms_),
ntt_tables_(copy.ntt_tables_),
slots_(copy.slots_),
qualifiers_(copy.qualifiers_)
{
int coeff_uint64_count = parms_.plain_modulus().uint64_count();
// Allocate and copy over roots of unity
roots_of_unity_ = allocate_poly(slots_, coeff_uint64_count, pool_);
set_poly_poly(copy.roots_of_unity_.get(), slots_, coeff_uint64_count, roots_of_unity_.get());
// Set mod_ and polymod_
mod_ = parms_.plain_modulus();
polymod_ = PolyModulus(parms_.poly_modulus().pointer(), parms_.poly_modulus().coeff_count(),
parms_.poly_modulus().coeff_uint64_count());
}
移动
PolyCRTBuilder(PolyCRTBuilder &&source) = default;
通过移动给定的一个来创建一个新的PolyCRTBuilder。
@param [in] source 要移动的PolyCRTBuilder
编码(compose)
uint64_t
void compose(const std::vectorstd::uint64_t &values, Plaintext &destination);
从给定矩阵创建SEAL明文。 该函数将以明文模数为模的给定整数矩阵“批处理”为SEAL明文元素,并将结果存储在目标参数中。 输入向量的大小必须至多等于多项式模数的大小。 元素的前半部分代表矩阵的第一行,后半部分代表第二行。 矩阵中的大小最大等于明文模数,以表示有效的SEAL明文。
@param [in] values 整数矩阵模数为明文模数
@param [out] destination 用结果覆盖的明文多项式
@throws std :: invalid_argument 如果值太大,抛出异常
void PolyCRTBuilder::compose(const vector<int64_t> &values_matrix, Plaintext &destination)
{
// Validate input parameters,验证输入的参数,如果矩阵的大小大于crtbuilder 的slot_counts,则报错。
if (values_matrix.size() > slots_)
{
throw logic_error("values_matrix size is too large");
}
#ifdef SEAL_DEBUG
for (auto v : values_matrix)
{
// Validate the i-th input
if (v >= mod_.value())
{
throw invalid_argument("input value is larger than plain_modulus");
}
}
#endif
// 当前输入矩阵的大小
int input_matrix_size = values_matrix.size();
// Set destination to full size,将明文多项式的目标位置预先设置为slot_counts
destination.resize(slots_);
// First write the values to destination coefficients. Read in top row, then bottom row. 不全的数组数据用0补充。
for (int i = 0; i < input_matrix_size; i++)
{
*(destination.pointer() + matrix_reps_index_map_[i]) = values_matrix[i];
}
for (int i = input_matrix_size; i < slots_; i++)
{
*(destination.pointer() + matrix_reps_index_map_[i]) = 0;
}
// Transform destination using inverse of negacyclic NTT
// Note: We already performed bit-reversal when reading in the matrix
inverse_ntt_negacyclic_harvey(destination.pointer(), ntt_tables_);
int64_t
void compose(const std::vectorstd::int64_t &values, Plaintext &destination);
同上,只类型不同。
从给定矩阵创建SEAL明文。 该函数将以明文模数为模的给定整数矩阵“批处理”为SEAL明文元素,并将结果存储在目标参数中。 输入向量的大小必须至多等于多项式模数的大小。 元素的前半部分代表矩阵的第一行,后半部分代表第二行。 矩阵中的数字最多等于明文模数,以表示有效的SEAL明文。
@param [in] values 整数矩阵模数为明文模数
@param [out] destination 用结果覆盖的明文多项式
@throws std :: invalid_argument 如果值太大
void PolyCRTBuilder::compose(const vector<int64_t> &values_matrix, Plaintext &destination)
{
// Validate input parameters
if (values_matrix.size() > slots_)
{
throw logic_error("values_matrix size is too large");
}
uint64_t plain_modulus_div_two = mod_.value() >> 1;
#ifdef SEAL_DEBUG
for (auto v : values_matrix)
{
// Validate the i-th input
if (abs(v) > plain_modulus_div_two)
{
throw invalid_argument("input value is larger than plain_modulus");
}
}
#endif
int input_matrix_size = values_matrix.size();
// Set destination to full size
destination.resize(slots_);
// First write the values to destination coefficients. Read
// in top row, then bottom row.
for (int i = 0; i < input_matrix_size; i++)
{
*(destination.pointer() + matrix_reps_index_map_[i]) = (values_matrix[i] < 0) ?
(mod_.value() + values_matrix[i]) : values_matrix[i];
}
for (int i = input_matrix_size; i < slots_; i++)
{
*(destination.pointer() + matrix_reps_index_map_[i]) = 0;
}
// Transform destination using inverse of negacyclic NTT
// Note: We already performed bit-reversal when reading in the matrix
inverse_ntt_negacyclic_harvey(destination.pointer(), ntt_tables_);
}
plaintext
void PolyCRTBuilder::compose(Plaintext &plain, const MemoryPoolHandle &pool)
从给定矩阵创建SEAL明文。该函数将给定的整数模量的整数给定矩阵“批处理”为准备加密的SEAL明文。矩阵作为明文元素给出,其前 N / 2 N / 2 N/2 系数表示矩阵的第一行,后 N / 2 N / 2 N/2系数表示第二行,其中 N N N 表示多项式模数的程度。输入明文必须具有小于多项式模数的递减,并且系数小于明文模数,即它必须是加密参数的有效明文。进程中的动态内存分配是从给定MemoryPoolHandle指向的内存池中分配的。
@param [in] plain整数矩阵模数为明文模数
@param [in] pool MemoryPoolHandle指向有效的内存池
如果plain对加密参数无效,则@throws std :: invalid_argument
如果池未初始化,则@throws std :: invalid_argument
* /
void PolyCRTBuilder::compose(Plaintext &plain, const MemoryPoolHandle &pool)
{
int coeff_count = parms_.poly_modulus().coeff_count();
// Validate input parameters
if (plain.coeff_count() > coeff_count ||
(plain.coeff_count() == coeff_count && plain[coeff_count - 1] != 0))
{
throw invalid_argument("plain is not valid for encryption parameters");
}
#ifdef SEAL_DEBUG
if (plain.significant_coeff_count() >= coeff_count || !are_poly_coefficients_less_than(plain.pointer(),
plain.coeff_count(), 1, parms_.plain_modulus().pointer(), parms_.plain_modulus().uint64_count()))
{
throw invalid_argument("plain is not valid for encryption parameters");
}
#endif
if (!pool)
{
throw invalid_argument("pool is uninitialized");
}
// We need to permute the coefficients of plain. To do this, we allocate
// temporary space.
int input_plain_coeff_count = min(plain.coeff_count(), slots_);
Pointer temp(allocate_uint(input_plain_coeff_count, pool));
set_uint_uint(plain.pointer(), input_plain_coeff_count, temp.get());
// Set plain to full slot count size.
plain.resize(slots_);
// First write the values to destination coefficients. Read
// in top row, then bottom row.
for (int i = 0; i < input_plain_coeff_count; i++)
{
*(plain.pointer() + matrix_reps_index_map_[i]) = temp[i];
}
for (int i = input_plain_coeff_count; i < slots_; i++)
{
*(plain.pointer() + matrix_reps_index_map_[i]) = 0;
}
// Transform destination using inverse of negacyclic NTT
// Note: We already performed bit-reversal when reading in the matrix
inverse_ntt_negacyclic_harvey(plain.pointer(), ntt_tables_);
}
解码(decompose)
uint64_t
void PolyCRTBuilder::decompose(const Plaintext &plain, vector<uint64_t> &destination, const MemoryPoolHandle &pool)
iverse of compose。 该函数将给定SEAL明文“解”为以明文模数为模的整数矩阵,并将结果存储在目标参数中。 输入明文必须具有小于多项式模数的递减,并且系数小于明文模数,即它必须是加密参数的有效明文。 进程中的动态内存分配是从给定MemoryPoolHandle指向的内存池中分配的。
@param [in] plain要解开的明文多项式
@param [out] destination要用槽的值覆盖的向量
@param [in] pool MemoryPoolHandle指向有效的内存池
如果plain对加密参数无效,则@throws std :: invalid_argument
如果池未初始化,则@throws std :: invalid_argument
void PolyCRTBuilder::decompose(const Plaintext &plain, vector<uint64_t> &destination, const MemoryPoolHandle &pool)
{
int coeff_count = parms_.poly_modulus().coeff_count();
// Validate input parameters
if (plain.coeff_count() > coeff_count ||
(plain.coeff_count() == coeff_count && plain[coeff_count - 1] != 0))
{
throw invalid_argument("plain is not valid for encryption parameters");
}
#ifdef SEAL_DEBUG
if (plain.significant_coeff_count() >= coeff_count || !are_poly_coefficients_less_than(plain.pointer(),
plain.coeff_count(), 1, parms_.plain_modulus().pointer(), parms_.plain_modulus().uint64_count()))
{
throw invalid_argument("plain is not valid for encryption parameters");
}
#endif
if (!pool)
{
throw invalid_argument("pool is uninitialized");
}
// Set destination size
destination.resize(slots_);
// Never include the leading zero coefficient (if present)
int plain_coeff_count = min(plain.coeff_count(), slots_);
Pointer temp_dest(allocate_uint(slots_, pool));
// Make a copy of poly
set_uint_uint(plain.pointer(), plain_coeff_count, temp_dest.get());
set_zero_uint(slots_ - plain_coeff_count, temp_dest.get() + plain_coeff_count);
// Transform destination using negacyclic NTT.
ntt_negacyclic_harvey(temp_dest.get(), ntt_tables_);
// Read top row
for (int i = 0; i < slots_; i++)
{
destination[i] = temp_dest[matrix_reps_index_map_[i]];
}
}
int64_t
void PolyCRTBuilder::decompose(const Plaintext &plain, vector<int64_t> &destination, const MemoryPoolHandle &pool)
同上
void PolyCRTBuilder::decompose(const Plaintext &plain, vector<int64_t> &destination,
const MemoryPoolHandle &pool)
{
int coeff_count = parms_.poly_modulus().coeff_count();
// Validate input parameters
if (plain.coeff_count() > coeff_count ||
(plain.coeff_count() == coeff_count && plain[coeff_count - 1] != 0))
{
throw invalid_argument("plain is not valid for encryption parameters");
}
#ifdef SEAL_DEBUG
if (plain.significant_coeff_count() >= coeff_count || !are_poly_coefficients_less_than(plain.pointer(),
plain.coeff_count(), 1, parms_.plain_modulus().pointer(), parms_.plain_modulus().uint64_count()))
{
throw invalid_argument("plain is not valid for encryption parameters");
}
#endif
if (!pool)
{
throw invalid_argument("pool is uninitialized");
}
// Set destination size
destination.resize(slots_);
// Never include the leading zero coefficient (if present)
int plain_coeff_count = min(plain.coeff_count(), slots_);
Pointer temp_dest(allocate_uint(slots_, pool));
// Make a copy of poly
set_uint_uint(plain.pointer(), plain_coeff_count, temp_dest.get());
set_zero_uint(slots_ - plain_coeff_count, temp_dest.get() + plain_coeff_count);
// Transform destination using negacyclic NTT.
ntt_negacyclic_harvey(temp_dest.get(), ntt_tables_);
// Read top row, then bottom row
uint64_t plain_modulus_div_two = mod_.value() >> 1;
for (int i = 0; i < slots_; i++)
{
int64_t curr_value = temp_dest[matrix_reps_index_map_[i]];
destination[i] = (curr_value > plain_modulus_div_two) ?
(curr_value - mod_.value()) : curr_value;
}
}
plaintext
void PolyCRTBuilder::decompose(Plaintext &plain, const MemoryPoolHandle &pool)
void PolyCRTBuilder::decompose(Plaintext &plain, const MemoryPoolHandle &pool)
{
int coeff_count = parms_.poly_modulus().coeff_count();
// Validate input parameters
if (plain.coeff_count() > coeff_count ||
(plain.coeff_count() == coeff_count && plain[coeff_count - 1] != 0))
{
throw invalid_argument("plain is not valid for encryption parameters");
}
#ifdef SEAL_DEBUG
if (plain.significant_coeff_count() >= coeff_count || !are_poly_coefficients_less_than(plain.pointer(),
plain.coeff_count(), 1, parms_.plain_modulus().pointer(), parms_.plain_modulus().uint64_count()))
{
throw invalid_argument("plain is not valid for encryption parameters");
}
#endif
if (!pool)
{
throw invalid_argument("pool is uninitialized");
}
// Never include the leading zero coefficient (if present)
int plain_coeff_count = min(plain.coeff_count(), slots_);
// Allocate temporary space to store a wide copy of plain
Pointer temp(allocate_uint(slots_, pool));
// Make a copy of poly
set_uint_uint(plain.pointer(), plain_coeff_count, temp.get());
set_zero_uint(slots_ - plain_coeff_count, temp.get() + plain_coeff_count);
// Transform destination using negacyclic NTT.
ntt_negacyclic_harvey(temp.get(), ntt_tables_);
// Set plain to full slot count size (note that all new coefficients are
// set to zero).
plain.resize(slots_);
// Read top row, then bottom row
for (int i = 0; i < slots_; i++)
{
*(plain.pointer() + i) = temp[matrix_reps_index_map_[i]];
}
}
其他
void PolyCRTBuilder::populate_roots_of_unity_vector()
void PolyCRTBuilder::populate_roots_of_unity_vector()
{
uint64_t generator_sq = multiply_uint_uint_mod(ntt_tables_.get_root(), ntt_tables_.get_root(), mod_);
roots_of_unity_[0] = ntt_tables_.get_root();
for (int i = 0; i < slots_ - 1; i++)
{
roots_of_unity_[i + 1] = multiply_uint_uint_mod(roots_of_unity_[i], generator_sq, mod_);
}
}
void PolyCRTBuilder::populate_matrix_reps_index_map()
void PolyCRTBuilder::populate_matrix_reps_index_map()
{
int logn = get_power_of_two(slots_);
uint32_t row_size = slots_ >> 1;
matrix_reps_index_map_.resize(slots_);
// Copy from the matrix to the value vectors
uint32_t gen = 3;
uint32_t pos = 1;
uint32_t m = slots_ << 1;
for (uint32_t i = 0; i < row_size; i++)
{
// Position in normal bit order
uint32_t index1 = (pos - 1) >> 1;
uint32_t index2 = (m - pos - 1) >> 1;
// Set the bit-reversed locations
matrix_reps_index_map_[i] = util::reverse_bits(index1, logn);
matrix_reps_index_map_[row_size | i] = util::reverse_bits(index2, logn);
// Next primitive root
pos *= gen;
pos &= (m - 1);
}
}
inline int slot_count()
inline int slot_count() const
{
return slots_;
}
inline void reverse_bits(std::uint64_t *input)
inline void reverse_bits(std::uint64_t *input)
{
#ifdef SEAL_DEBUG
if (input == nullptr)
{
throw std::invalid_argument("input cannot be null");
}
#endif
std::uint32_t n = parms_.poly_modulus().coeff_count() - 1;
int logn = util::get_power_of_two(n);
for (std::uint32_t i = 0; i < n; i++)
{
std::uint32_t reversed_i = util::reverse_bits(i, logn);
if (i < reversed_i)
{
std::swap(input[i], input[reversed_i]);
}
}
}