SEAL中CRT 編碼相關實現及說明
終於還是要讀源碼了,終究擺脫不了數學的噩夢,先從讀懂他們的代碼開始。
文件原位置SEAL/Polycrt.h
介紹
提供CRT批處理功能。 如果多項式模數是 ,並且明文模數是素數 使得 ,則PolyCRTBuilder允許SEAL明文元素被視爲 的矩陣,在這種加密矩陣上執行的同態操作是應用係數(槽),爲可矢量化的計算啓用強大的SIMD功能。 此功能通常在同態加密文獻中稱爲“批處理”。
-
par數學背景
從數學上講,如果poly_modulus是 , 是 的冪,plain_modulus是素數 ,使得 整除 ,則模的整數包含 一個 的原始根,多項式分裂成個不同的線性因子,如,其中常數 都是不同的原始的第個整數的根在整數模中。中國剩餘定理(CRT)表明在這種情況下明文空間與同構(作爲代數)摺疊領域的直接產品。同構很容易在兩個方向上明確計算,這就是這個類的作用。此外,擴張的伽羅瓦羣是,其對原始根的作用很容易描述。由於批處理時隙與統一的原始根對應1對1,因此通過置換時隙在明文上應用Galois自同構。通過應用伽羅瓦羣的兩個循環子羣的生成器,我們可以有效地將明文視爲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明文。矩陣作爲明文元素給出,其前 係數表示矩陣的第一行,後係數表示第二行,其中 表示多項式模數的程度。輸入明文必須具有小於多項式模數的遞減,並且係數小於明文模數,即它必須是加密參數的有效明文。進程中的動態內存分配是從給定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]);
}
}
}