文章目錄
- 一、batch_encoder (用于 BFV)
- 1. 概述
- 2. 數學原理
- 3. 使用方法
- 4. 代碼示例
- 二、ckks_encoder (用于 CKKS)
?在 1. bfv_basics.cpp
中,我們展示了如何使用BFV方案執行非常簡單的計算。計算是在 plain_modulus
參數的模下執行的,并且 只使用了 BFV 明文多項式的一個系數。這種方法有兩個顯著的問題:
- 實際應用通常使用整數或實數算術,而不是模算術;
- 我們只使用了明文多項式的一個系數。這真的很浪費,因為明文多項式很大,無論如何都會被完整加密。
對于 (1),人們可能會問為什么不簡單地 增加 plain_modulus 參數 直到不發生溢出,并且計算表現得像整數算術一樣。問題是增加 plain_modulus 會 增加噪聲預算消耗,并且也會 減少初始噪聲預算。
?在這些示例中,我們將討論將數據布局到 明文元素 中的其他方法(編碼),這些方法允許更多計算而不發生數據類型溢出,并且可以允許 充分利用整個明文多項式。
一、batch_encoder (用于 BFV)
1. 概述
?Batch Encoder 的目標是什么?通常,一個同態加密的密文只加密一個明文值。如果我們想對 兩個數組 (向量) 逐項相加,就需要為每個元素單獨加密成一個密文,然后對每對密文分別進行同態加法,這非常低效。
?Batch Encoder 的作用就是將一個明文向量,比如 [a1,a2,...,ak][a_1, a_2, ..., a_k][a1?,a2?,...,ak?] 打包編碼到一個 單一的明文多項式 中。然后對這個單一多項式進行加密,得到一個 單一的密文。之后所有同態運算都作用在這一個密文上。一般稱之為 SIMD 策略,這極大地提高了同態加密的吞吐量和效率。
?設 N 表示 poly_modulus_degree,T 表示 plain_modulus。批處理 允許將 BFV 明文多項式 視為 2×(N/2) 矩陣,每個元素 都是 模 T 的整數。
?在 矩陣視圖 中,加密操作對加密矩陣 按元素執行,允許用戶在完全可向量化的計算中獲得幾個數量級的加速。
?因此,除了最簡單的計算外,批處理應該是與 BFV 一起使用的首選方法,當正確使用時,將產生優于任何不使用批處理的實現。
2. 數學原理
?核心思想是,如果一個 模多項式 (在 BFV 中即 xN+1x^N+1xN+1) 可以其系數環 (這里是 ZtZ_tZt?) 上分解為多個 兩兩互質的因子,那么原始的、復雜的商環就與這些因子定義的更簡單的商環的 直積 (Direct Product) 存在一種被稱為 同構 的深刻聯系。
- 中國剩余定理:
?Batch Encoder 的核心是基于 中國剩余定理 的多項式環分解。基本概念為:- BFV 明文文空間是多項式環 Zt[x]xN+1\frac{Z_t[x]}{x^N+1}xN+1Zt?[x]?。
- 其中 N=poly_modulus_degreeN=poly\_modulus\_degreeN=poly_modulus_degree,t=plain_modulust=plain\_modulust=plain_modulus。
- 當 t 是 滿足特定條件的素數 時,這個環可以分解。這個條件就是 t≡1(mod2N)t \equiv 1\ \ (mod\ 2N)t≡1??(mod?2N)當滿足這個條件時,多項式 xN+1x^N+1xN+1 可以完全分解為 N 個不同的線性因子。
- 環的分解:
?當上面的條件滿足的時候,我們有:Zt[x]xN+1?Zt×Zt×...×Zt(N個副本)\frac{Z_t[x]}{x^N+1} \cong Z_t \times Z_t \times ... \times Z_t\ \ \ (N 個副本)xN+1Zt?[x]??Zt?×Zt?×...×Zt????(N個副本)?\cong? 是同構的意思,表示兩個代數結構是同構的,即它們在結構上是相同的,盡管它們的元素可能不同。
?這意味著:- 一個度數小于 N 的多項式 <—> N 個獨立的模 t 整數。
- 多項式運算 <—> 對應位置的元素運算。
- 實現了 SIMD 計算。
- 矩陣視圖:
?在 SEAL 中,N 個槽被組織為 2×(N/2)2 \times (N / 2)2×(N/2) 矩陣:
[ slot_0, slot_1, ..., slot_(N/2-1) ]
[ slot_(N/2), ..., slot_(N-1) ]
3. 使用方法
- 第一步當然是設置 加密參數,其中加密方案、多項式模數次數、密文系數模數何止前的思路一樣。但是要求將
plain_modulus
設置為一個 與1
模2*poly_modulus_degree
同余的素數。這一步 Microsoft SEAL 提供了一個輔助方法來尋找這樣的素數。在這個示例中,我們創建一個支持批處理的 20 位素數。
parms.set_plain_modulus(PlainModulus::Batching(poly_modulus_degree, 20)); // 設置支持批處理的明文模數
- 用設置好的參數生成上下文;
- 生成
KeyGenerator
、Encryptor
、Evaluator
、Decryptor
; - 生成
BatchEncoder
的實例,批處理 '槽’的總數 等于poly_modulus_degree = N
,這些槽被組織成2×(N/2)
矩陣,可以進行加密和計算。每個槽 包含一個 模plain_modulus
的整數; - 先將數據存到
vector<uint64_t> pod_matrix(slot_count, 0ULL)
向量中。然后通過BatchEncoder類
提供的方法.encode
,將矩陣編碼為明文Plaintext類
的實例中; - 解碼的過程完全相反,通過
BatchEncoder類
提供的.decode
方法,將Plaintext類
明文解碼為vector<uint64_t>
向量。 - 對明文進行加密和運算的方法同之前一摸一樣。
4. 代碼示例
print_example_banner("Example: Encoders / Batch Encoder");EncryptionParameters parms(scheme_type::bfv); // 創建BFV方案的加密參數對象size_t poly_modulus_degree = 8192; // 設置多項式模數次數為8192parms.set_poly_modulus_degree(poly_modulus_degree); // 應用多項式模數次數設置parms.set_coeff_modulus(CoeffModulus::BFVDefault(poly_modulus_degree)); // 設置系數模數為BFV默認值parms.set_plain_modulus(PlainModulus::Batching(poly_modulus_degree, 20)); // 設置支持批處理的明文模數SEALContext context(parms); // 使用參數創建SEAL上下文對象KeyGenerator keygen(context); // 創建密鑰生成器對象SecretKey secret_key = keygen.secret_key(); // 生成私鑰PublicKey public_key; // 聲明公鑰對象keygen.create_public_key(public_key); // 創建公鑰RelinKeys relin_keys; // 聲明重線性化密鑰對象keygen.create_relin_keys(relin_keys); // 創建重線性化密鑰Encryptor encryptor(context, public_key); // 創建加密器,使用公鑰Evaluator evaluator(context); // 創建計算器Decryptor decryptor(context, secret_key); // 創建解密器,使用私鑰BatchEncoder batch_encoder(context); // 創建批處理編碼器對象size_t slot_count = batch_encoder.slot_count(); // 獲取槽的總數size_t row_size = slot_count / 2; // 計算每行的大小cout << "明文矩陣行大小: " << row_size << endl;vector<uint64_t> pod_matrix(slot_count, 0ULL); // 創建大小為slot_count的向量,初始化為0pod_matrix[0] = 0ULL; // 設置第一行第一個元素pod_matrix[1] = 1ULL; // 設置第一行第二個元素pod_matrix[2] = 2ULL; // 設置第一行第三個元素pod_matrix[3] = 3ULL; // 設置第一行第四個元素pod_matrix[row_size] = 4ULL; // 設置第二行第一個元素pod_matrix[row_size + 1] = 5ULL; // 設置第二行第二個元素pod_matrix[row_size + 2] = 6ULL; // 設置第二行第三個元素pod_matrix[row_size + 3] = 7ULL; // 設置第二行第四個元素Plaintext plain_matrix; // 聲明明文對象batch_encoder.encode(pod_matrix, plain_matrix); // 將矩陣編碼為明文vector<uint64_t> pod_result; // 聲明結果向量batch_encoder.decode(plain_matrix, pod_result); // 解碼明文到向量print_matrix(pod_result, row_size); // 打印結果矩陣Ciphertext encrypted_matrix; // 聲明密文對象encryptor.encrypt(plain_matrix, encrypted_matrix); // 加密明文/*對密文的操作會導致在所有8192個槽(矩陣元素)中同時執行同態操作。為了說明這一點,我們構造另一個明文矩陣[ 1, 2, 1, 2, 1, 2, ..., 2 ][ 1, 2, 1, 2, 1, 2, ..., 2 ]并將其編碼為明文。*/vector<uint64_t> pod_matrix2; // 聲明第二個矩陣向量for (size_t i = 0; i < slot_count; i++) // 循環填充向量pod_matrix2.push_back((i & size_t(0x1)) + 1); // 使用位運算生成1,2交替的模式Plaintext plain_matrix2; // 聲明第二個明文對象batch_encoder.encode(pod_matrix2, plain_matrix2); // 編碼第二個矩陣print_matrix(pod_matrix2, row_size); // 第二個輸入明文矩陣evaluator.add_plain_inplace(encrypted_matrix, plain_matrix2); // 將密文與明文相加(就地操作)evaluator.square_inplace(encrypted_matrix); // 對密文進行平方(就地操作)evaluator.relinearize_inplace(encrypted_matrix, relin_keys); // 重線性化以減少密文大小/*我們還剩多少噪聲預算?*/cout << " + 結果中的噪聲預算: " << decryptor.invariant_noise_budget(encrypted_matrix) << " 位" << endl;/*我們解密并分解明文以恢復矩陣形式的結果。*/Plaintext plain_result; // 聲明結果明文對象print_line(__LINE__);cout << "解密并解碼結果。" << endl;decryptor.decrypt(encrypted_matrix, plain_result); // 解密密文batch_encoder.decode(plain_result, pod_result); // 解碼明文到向量cout << " + 結果明文矩陣 ...... 正確。" << endl;print_matrix(pod_result, row_size);
二、ckks_encoder (用于 CKKS)
暫時掠過