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: に対応, 多項式の次元を表す
  • 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回する度に scaleP_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)
    • 1scale = 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);

参考文献