# $Paillier$ 同態加密方案
背景
生成公鑰金鑰
隨機選取大素數 \(p,\ q\),保證 \((pq,\ (p - 1)\cdot (q - 1)) = 1\),計算 \(n = pq,\ \lambda = [p - 1,\ q - 1]\)
隨機選取 \(g\in \mathbb{Z_{n^2}^{*}}\),計算 \(\mu = [L(g^{\lambda}\ mod\ n^2)]^{-1}\ mod\ n\),其中 \(L(x) = \frac{x - 1}{n}\)
其中 \((n,\ g)\) 為公鑰,\((\lambda,\ \mu)\) 為私鑰
加密
將明文 \(m\) 對映成 \(0\leq m < n\)
選取 \(r\in \mathbb{Z_{n}^{*}},\ 0 < r < n\),即 \((r,\ n) = 1\)
計算密文 \(c = g^{m}\cdot r^{n}\ (mod\ n^2)\)
解密
計算 \(m = L(c^{\lambda}\ mod\ n^2)\cdot \mu\ (mod\ n)\)
正確性
由 \(\lambda = [p - 1,\ q - 1]\),所以對於 \(x\in \mathbb{Z_{n}}\),滿足 \(x^{\lambda}\equiv 1\ (mod\ p),\ x^{\lambda}\equiv 1\ (mod\ q)\)
所以有 \(g^{\lambda}\equiv 1\ (mod\ n),\ r^{\lambda}\equiv 1\ (mod\ n)\)
不妨設 \(g = 1 + qn,\ r = 1 + q'n\),滿足 \(q,\ q'\in \mathbb{Z}\)
計算
\[\begin{aligned} c^{\lambda} & \equiv g^{m\lambda}\cdot r^{n\lambda}\ (mod\ n^2)\\ & \equiv (1 + qn)^{m}\cdot (1 + q'n)^{n}\ (mod\ n^2)\\ & \equiv (1 + qnm)\cdot (1 + q'n^2)\ (mod\ n^2)\\ & \equiv 1 + qnm\ (mod\ n^2) \end{aligned} \]所以
\[\frac{c^{\lambda} - 1}{n}\equiv qm\ ( mod\ n^2) \]同理計算出
\[\frac{g^{\lambda} - 1}{n}\equiv q\ (mod\ n^2) \]所以
\[m\equiv \frac{\frac{c^{\lambda} - 1}{n}\ mod\ n^2}{\frac{g^{\lambda} - 1}{n}\ mod\ n^2}\equiv qm\cdot q^{-1}\equiv m\ (mod\ n) \]隨機選取 \(g\)
令 \(g = 1 + n\)
則 \((1 + n)^n\equiv 1 + n^2\equiv 1\ (mod\ n^2)\),這意味著 \(ord(1 + n)\mid n\)
而對於 \(1 < k < n\),有 \((1 + n)^k\equiv 1 + kn\not\equiv 1\ (mod\ n^2)\),所以 \(ord(1 + n) = n\)
進一步,令 \(g = 1 + kn,\ 0 < k < n\)
則 \((1 + kn)^n\equiv 1\ (mod\ n^2)\)
對於 \(1 < k' < n\),有 \((1 + kn)^{k'}\equiv 1 + k'kn\ (mod\ n^2)\),只需要 \(kk'\not\equiv 0\ (mod\ n)\) 即可,即
\[k\not\equiv p\ (mod\ n),\ k\not\equiv q\ (mod\ n) \]所以對 \(g\) 的隨機選取,轉化為對 \(k\in \mathbb{Z_{n}}\backslash\left\{p,\ q\right\}\) 的隨機選取
程式碼
#include <bits/stdc++.h>
#define LL long long
#define pb push_back
using namespace std;
const int L = 10;
const int m1 = 15, m2 = 20;
int n, p, q, lambda, mu;
LL g, nn;
vector<int> ZnStar;
LL qmul(LL a, LL b, LL mod)
{
LL res = 0;
while(b > 0) {
if(b & 1) res += a, res %= mod;
a += a, a %= mod;
b >>= 1;
}
return res;
}
LL qpow(LL a, LL b, LL MOD)
{
if(!a) return 0;
LL ans = 1;
while(b) {
if(b & 1) ans = qmul(ans, a, MOD), ans %= MOD;
a = qmul(a, a, MOD), a %= MOD, b >>= 1;
}
return ans;
}
LL phi(LL m)
{
LL ans = m;
for(LL i = 2; i * i <= m; ++i) {
if(m % i == 0) {
ans -= ans / i;
while(m % i == 0) m /= i;
}
}
if(m > 1) ans -= ans / m;
return ans;
}
LL getInv(LL a, LL MOD)
{
return qpow(a, phi(MOD) - 1, n);
}
bool isPrime(int x)
{
if(x == 1) return 0;
for(int i = 2; i * i <= x; ++i)
if(x % i == 0) return 0;
return 1;
}
int getPrime(int L)
{
int ans = 0;
do {
ans = 1;
for(int i = 0; i < L - 2; ++i)
ans *= 2, ans += rand() % 2;
ans *= 2, ans += 1;
}while(!isPrime(ans));
return ans;
}
void getZnStar(int n)
{
for(int i = 1; i < n; ++i)
if(__gcd(i, n) == 1 && i != p && i != q)
ZnStar.pb(i);
}
int getNumOfZnStar() { return ZnStar[rand() % ZnStar.size()]; }
int getLambda(int p, int q) { return (p - 1) * (q - 1) / __gcd(p - 1, q - 1); }
void getPublicKey()
{
while(1) {
p = getPrime(L);
q = getPrime(L);
lambda = getLambda(p, q);
n = p * q;
if(__gcd(n, lambda) == 1 && p != q) break;
}
nn = 1LL * n * n;
getZnStar(n);
g = (1 + 1LL * n * getNumOfZnStar()) % nn;
cout << "n: " << n << endl;
cout << "p: " << p << endl;
cout << "q: " << q << endl;
cout << "g: " << g << endl;
cout << "n^2: " << nn << endl;
}
LL getLx(LL x) { return (x - 1) / n; }
void getPrivateKey()
{
LL x = qpow(g, lambda, nn);
LL Lx = getLx(x);
mu = getInv(Lx, n);
cout << "lambda: " << lambda << endl;
cout << "mu: " << mu << endl;
}
LL encryption(int m)
{
LL r = getNumOfZnStar();
LL c = qmul(qpow(g, m, nn), qpow(r, n, nn), nn);
cout << "r: " << r << endl;
cout << "c: " << c << endl;
return c;
}
LL decryption(LL c)
{
cout << c << endl;
LL temp = qpow(c, lambda, nn);
LL Lx = getLx(temp) % n;
LL plaintext = Lx * mu % n;
cout << "plaintext: " << plaintext << endl;
return plaintext;
}
int main()
{
srand((unsigned)time(NULL));
getPublicKey();
getPrivateKey();
LL ciphertext1 = encryption(m1);
LL ciphertext2 = encryption(m2);
LL plaintext = decryption(qmul(ciphertext1, ciphertext2, nn));
return 0;
}