同态加密Hoisting Rotation介绍和OpenFHE示例
摘要
Hoisting在一个密文需要旋转多次的时候,可以预先计算公共的部分,从而加速整个旋转操作。
本文简要介绍RLWE同态加密的旋转(Rotation)操作和Hoisting的优化原理,然后给出如何在OpenFHE同态加密库中使用Hoisting Rotation。
密文旋转
RLWE同态密文旋转主要有下面三步:
- 自同态:通过自同态将密文槽旋转,即计算 ϕ ( c t ) \phi(ct) ϕ(ct);
- 数值分解(break into digits):将密文的系数用 δ \delta δ进制表示 ϕ ( c t ) = ∑ q i δ i \phi(ct)=\sum q_i \delta^i ϕ(ct)=∑qiδi。其中 δ \delta δ是一个小的整数, q i q_i qi的系数小于 δ \delta δ;
- 密钥交换:将旋转后的密文的解密密钥 ϕ ( s k ) \phi(sk) ϕ(sk),通过密钥交换,变为原来的密钥 s k sk sk。
密文旋转的本质就是一个自同态映射 ϕ \phi ϕ。我们使用 c t ct ct表示密文,那么旋转后的密文 c t 1 = ϕ ( c t ) ct_1=\phi(ct) ct1=ϕ(ct). 这时的密文中的明文已经移到对应的位置了,但是解密密钥却变为了 ϕ ( s k ) \phi(sk) ϕ(sk).
因此,第2、3步是为了将解密密钥重新切换为 s k sk sk.
第2步的目的是降低密钥交换的噪声。
Hoisting Rotation
Hoisting Rotation 可以翻译成提升旋转,或者快速旋转。通过将多个旋转的公共部分计算,提到前面计算,降低整体的开销。为此,我们需要对上面的密文旋转步骤做调整。我们先计算第2步,然后再计算第1步,最后计算第3步:
- 数值分解:首先对原来的密文分解 c t = ∑ q i ′ δ i ct=\sum q_i^\prime \delta^i ct=∑qi′δi;
- 自同态:对分解后的密文进行自同态计算 ϕ ( q i ′ ) \phi(q_i^\prime) ϕ(qi′);
- 密钥交换:使用 ϕ ( q i ′ ) \phi(q_i^\prime) ϕ(qi′)进行密钥交换。
这里的正确性由下面两点保证:
- 自同态对加法和乘法具有分配律,即 ϕ ( λ 1 a + λ 2 b ) = λ 1 ϕ ( a ) + λ 2 ϕ ( b ) \phi(\lambda_1a+\lambda_2b)=\lambda_1\phi(a)+\lambda_2\phi(b) ϕ(λ1a+λ2b)=λ1ϕ(a)+λ2ϕ(b),其中 λ 1 , λ 2 \lambda_1,\lambda_2 λ1,λ2是常数;
- 自同态不会明显增加环上多项式的范数,也就是,如果 q < δ q<\delta q<δ,那么 ϕ ( q ) < λ δ \phi(q)<\lambda \delta ϕ(q)<λδ,其中 λ \lambda λ是一个与环相关的常数。
所以,我们有:
ϕ
(
c
t
)
=
ϕ
(
∑
q
i
′
δ
i
)
=
∑
ϕ
(
q
i
′
)
δ
i
\phi(ct)=\phi(\sum q_i^\prime \delta^i)=\sum \phi(q_i^\prime)\delta^i
ϕ(ct)=ϕ(∑qi′δi)=∑ϕ(qi′)δi.
并且, ϕ ( q i ′ ) \phi(q_i^\prime) ϕ(qi′)是的系数是小整数。
这样第一步对于所有的旋转步数来说,都是一样的,可以提到前面进行计算。
但是这里存在一个问题,就是原来的旋转的自同态只需要执行一次,而交换顺序后,自同态需要作用到每一个 q i ′ q^\prime_i qi′。
所幸的是,多项式在大多数的密码学库都是采用的DCRT的形式表示(系数使用整数中国剩余定理分解,多项式使用多项式的中国剩余定理分解)。在这种表示下,自同态是非常快的。而数值分解需要进行多项式表示的转换,耗费的时间比较多。
因此,Hoisting Rotation可以加快整体的旋转。
OpenFHE代码示例
// OpenFHE example by zyf
#include<iostream>
#include<time.h>
#include <chrono>
#include "openfhe.h"
using namespace std;
using namespace lbcrypto;
// This example shows that how to rotation in CKKS,
// and how to use hoisting rot
int main(){
// var for computation time
chrono::high_resolution_clock::time_point time_start, time_end;
chrono::microseconds time_diff;
CCParams<CryptoContextCKKSRNS> parameters;
parameters.SetMultiplicativeDepth(2);
parameters.SetScalingModSize(40);
CryptoContext<DCRTPoly> cc=GenCryptoContext(parameters);
cc->Enable(PKE);
cc->Enable(KEYSWITCH);
cc->Enable(LEVELEDSHE);
usint cycOrder=cc->GetCyclotomicOrder();
cout<<"The order of ring is "<<cycOrder<<endl;
KeyPair<DCRTPoly> kp=cc->KeyGen();
//just rot 1,2,3,4,5,6,7,8 steps
cc->EvalRotateKeyGen(kp.secretKey,{1,2,3,4,5,6,7,8});
size_t num=8;
vector<double> vec_double={1,2,3,4,5,6,7,8};
Plaintext p1=cc->MakeCKKSPackedPlaintext(vec_double);
auto c0=cc->Encrypt(kp.publicKey,p1);
vector<Ciphertext<DCRTPoly>> rotated_c(num);
vector<Ciphertext<DCRTPoly>> hoist_rotated_c(num);
cout<<"Rotation"<<endl;
time_start=chrono::high_resolution_clock::now();
for(size_t i=0;i<num;i++){
rotated_c[i]=cc->EvalRotate(c0,i+1);
}
time_end=chrono::high_resolution_clock::now();
time_diff = chrono::duration_cast<chrono::microseconds>(time_end - time_start);
cout << "The time is [" << time_diff.count() << " microseconds]" << endl;
Plaintext result;
for(size_t i=0;i<num;i++){
cc->Decrypt(kp.secretKey,rotated_c[i],&result);
result->SetLength(num);
cout<<result<<endl;
}
cout<<"Hoisting Rotation"<<endl;
time_start=chrono::high_resolution_clock::now();
auto pre_c=cc->EvalFastRotationPrecompute(c0);
for(size_t i=0;i<num;i++){
hoist_rotated_c[i]=cc->EvalFastRotation(c0,i+1,cycOrder,pre_c);
}
time_end=chrono::high_resolution_clock::now();
time_diff = chrono::duration_cast<chrono::microseconds>(time_end - time_start);
cout << "The time is [" << time_diff.count() << " microseconds]" << endl;
for(size_t i=0;i<num;i++){
cc->Decrypt(kp.secretKey,rotated_c[i],&result);
result->SetLength(num);
cout<<result<<endl;
}
}
在我的机器上,普通的旋转8次,需要93613 microseconds,而Hoisting Rotation需要60798 microseconds。
原文地址:https://blog.csdn.net/watqw/article/details/142413056
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!