Microsoft SEAL
example
setup
#include "seal/seal.h"
#include <vector>
using namespace std;
using namespace seal;
int main() {
EncryptionParameters parms(scheme_type::ckks);
# N
size_t poly_modulus_degree = 8192;
parms.set_poly_modulus_degree(poly_modulus_degree);
# scale
double scale = pow(2.0, 40);
# modulus chain
parms.set_coeff_modulus(CoeffModulus::Create(poly_modulus_degree, { 60, 40, 40, 60 }));
...
poly_modulus_degree: N に対応, 多項式の次元を表す
scale: 多項式のスケールを表す
modulus_chain: modulus chain
keygen
SEALContext context(parms);
KeyGenerator keygen(context);
# sk
auto secret_key = keygen.secret_key();
# pk
PublicKey public_key;
keygen.create_public_key(public_key);
# rlk
RelinKeys relin_keys;
keygen.create_relin_keys(relin_keys);
# rotk
GaloisKeys gal_keys;
keygen.create_galois_keys(gal_keys);
Encryptor encryptor(context, public_key);
Evaluator evaluator(context);
Decryptor decryptor(context, secret_key);
encode -> encrypt
CKKSEncoder encoder(context);
size_t slot_count = encoder.slot_count();
// constant
Plaintext plain_coeff3, plain_coeff1, plain_coeff0;
encoder.encode(3.14159265, scale, plain_coeff3);
encoder.encode(0.4, scale, plain_coeff1);
encoder.encode(1.0, scale, plain_coeff0);
// input -(encode)-> x_plain -(encrypt)-> x1_encrypted
vector<double> input;
input.reserve(slot_count);
for (size_t i = 0; i < slot_count; i++) {
input.push_back(1.0);
}
Plaintext x_plain;
encoder.encode(input, scale, x_plain);
Ciphertext x1_encrypted;
encryptor.encrypt(x_plain, x1_encrypted);
evaluate
- modulus_chainが
{ 60, P_1 = 40, P_2 = 40, (P_3) } なのでlevel = 2. つまり, 2回までrescale() 出来, 1回する度に scale が P_1 ≒ P_2 ≒ 2^40 程度下がることに注意
- scaleが完全に一致していないと評価時に例外が出る
// eval πx^3 + 0.4x + 1
// 1. eval πx^3
// 1.1 eval x^2 then relin and rescale
Ciphertext x3_encrypted;
// x^2: scale ≒ 2^80, level = 2
evaluator.square(x1_encrypted, x3_encrypted);
evaluator.relinearize_inplace(x3_encrypted, relin_keys);
// x^2: scale ≒ 2^80 / P_2, level = 1
evaluator.rescale_to_next_inplace(x3_encrypted);
// 1.2 eval πx then relin and rescale
Ciphertext x1_encrypted_coeff3;
// πx: scale ≒ 2^80, level = 2
evaluator.multiply_inplace(x3_encrypted, x1_encrypted_coeff3);
// πx: scale ≒ 2^80 / P_2, level = 1
evaluator.relinearize_inplace(x3_encrypted, relin_keys);
// 1.3 eval πx * x^2
// πx^3: scale ≒ (2^80 / P_2)^2, level = 1
evaluator.multiply_inplace(x3_encrypted, x1_encrypted_coeff3);
evaluator.relinearize_inplace(x3_encrypted, relin_keys);
// πx^3: scale ≒ (2^80 / P_2)^2 / P_1, level = 0
evaluator.rescale_to_next_inplace(x3_encrypted);
// 2. eval 0.4x
evaluator.multiply_plain_inplace(x1_encrypted, plain_coeff1);
// 0.4x: scale ≒ 2^80 / P_2, level = 1
evaluator.rescale_to_next_inplace(x1_encrypted);
// 3. eval πx^3 + 0.4x + 1
// 1st, 2nd, 3rd term's scale ≒ 2^40, but not same.
scaleを合わせる方法
- 手動
2^120 / (P_2 ^ 2 * P_1) ≒ 1 なので許容してscaleを変更する
x1_encrypted.scale() = pow(2.0, 40)
1 を scale = 2^80 / P_2 でencodeしてかけて P_1 でrescale
- mos switchをする
- rescaleをせずにmodulusを合わせるために
parms_id_type last_parms_id = x3_encrypted.parms_id();
evaluator.mod_switch_to_inplace(x1_encrypted, last_parms_id);
evaluator.mod_switch_to_inplace(plain_coeff0, last_parms_id);
Ciphertext encrypted_result;
evaluator.add(x3_encrypted, x1_encrypted, encrypted_result);
evaluator.add_plain_inplace(encrypted_result, plain_coeff0);
decrypt -> decode
Plaintext plain_result;
decryptor.decrypt(encrypted_result, plain_result);
encoder.decode(plain_result, result);
参考文献