\(1.\) 背景
\(1.1\) 生成公鑰密鑰
隨機選取大素數 \(p,\ q\),計算 \(n = pq,\ \lambda = [p - 1,\ q - 1]\),保證 \((pq,\ (p - 1)\cdot (q - 1)) = 1\),即 \((n,\ \phi(n)) = 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)\) 為公鑰,\((p,\ q,\ \lambda)\) 為私鑰
多說一嘴,很多人無法理解為何保證了 \((p,\ q) = 1\),還要保證 \((n,\ \phi(n)) = 1\),事實上如果構造出 \(p = kq + 1\),那么 \(n\) 和 \(\phi(n)\) 就出現了 \(q\) 這個公因子
\(1.2\) 加密
將明文 \(m\) 映射成正整數
選取 \(r\in \mathbb{Z_{n}^{*}}\),這意味着 \((r,\ n) = 1\)
計算密文 \(c = g^{m}\cdot r^{n}\ (mod\ n^2)\)
\(1.3\) 解密
計算 \(m = L(c^{\lambda}\ mod\ n^2)\cdot \mu\ (mod\ n)\)
\(1.4\) 正確性
\(paillier\) 算法的正確性基於以下事實
由
所以對於 \(\forall v\in \mathbb{Z}\),滿足
所以 \(v^{\lambda}\equiv 1\ (mod\ n)\),這是因為 \(n = pq\) 且 \((p, q) = 1\)
所以有 \(g^{\lambda}\equiv 1\ (mod\ n),\ r^{\lambda}\equiv 1\ (mod\ n)\)
不妨設 \(g^{\lambda} = 1 + qn,\ r^{\lambda} = 1 + q'n\),滿足 \(q,\ q'\in \mathbb{Z}\)
計算
所以
同理計算出
所以
\(1.5\) 隨機選取 \(g\)
令 \(g = 1 + n\)
則 \((1 + n)^n\equiv 1 + n^2\equiv 1\ (mod\ n^2)\),這意味着 \(ord(1 + n)\mid n\)
而對於 \(0 < 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)\)
對於 \(0 < k' < n\),有 \((1 + kn)^{k'}\equiv 1 + k'kn\ (mod\ n^2)\),只需要 \(kk'\not\equiv 0\ (mod\ n)\) 即可,即
所以對 \(g\) 的隨機選取,轉化為對 \(k\in \mathbb{Z_{n}^{*}}\) 的隨機選取
\(1.6\) 加法同態性
考慮加密 \(m_{1} + m_{2}\),得到
在考慮分別加密 \(m_{1},\ m_{2}\),得到
所以
考慮解密 \(c_{1}c_{2}\),設 \(g^{\lambda} = 1 + qn,\ q\in \mathbb{Z_{n}}^{*}\)
剩下的計算與之前類似,解密得到明文為 \(m_{1} + m_{2}\)
也就是說 \(c_{1}c_{2}\) 的解密結果為 \(m_{1} + m_{2}\),即
滿足加法同態
至於為什么是加法同態,是因為我們對明文的操作是加法
\(2.\) 問題描述
- 隨機選擇兩個 \(10\) 位素數,這里指的是 \(10\) 位二進制串
- 令 \(n = pq\),隨機選擇 \(g\),計算公鑰和私鑰
- 設消息 \(m_{1} = 15,\ m_{2} = 20\),隨機選取 \(r_{1},\ r_{2}\in \mathbb{Z_{n}^{*}}\),求 \(m_{1},\ m_{2}\) 的密文 \(c_{1},\ c_{2}\)
- 對 \(c_{1},\ c_{2}\) 進行解密,求 \(m'_{1},\ m'_{2}\)
- 對 \(c_{1}c_{2}\) 做解密運算,得到 \(m'\),驗證 \(m' = m_{1} + m_{2}\)
\(3.\) 問題解與分析
\(3.1\) 隨機構造兩個 \(10\) 位素數 \(p,\ q\)
用字符串生成,轉化為整數,並判斷是否是素數,注意字符串的首末位都為 \(1\)
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;
}
\(3.2\) 隨機選擇 \(g\),計算公鑰和私鑰
\(g = 1 + kn,\ k\in \mathbb{Z_{n}^{*}}\),對 \(g\) 的隨機轉化為對 \(k\) 的隨機
先篩出 \(\mathbb{Z_{n}^{*}}\),然后隨機返回 \(k\in \mathbb{Z_{n}^{*}}\),計算具體的公鑰和密鑰
struct PBK
{
int n;
LL g;
PBK() {}
PBK(int _n, LL _g): n(_n), g(_g) {}
void out() { cout << "公鑰 <n, g>: <" << n << ", " << g << ">" << endl; }
};
struct SK
{
int p, q, lambda;
SK() {}
SK(int _p, int _q, int _lambda): p(_p), q(_q), lambda(_lambda) {}
void out() { cout << "私鑰 <p, q, lambda>: <" << p << ", " << q << ", " << lambda << ">" << endl; }
};
void getZnStar(vector<int>& ZnStar, int n)
{
for(int i = 1; i < n; ++i)
if(__gcd(i, n) == 1)
ZnStar.pb(i);
}
int getNumOfZnStar(vector<int>& ZnStar) { return ZnStar[rand() % ZnStar.size()]; }
int getLambda(int p, int q) { return (p - 1) * (q - 1) / __gcd(p - 1, q - 1); }
pair<PBK, SK> getPublicKey(int L, vector<int>& ZnStar)
{
int n, p, q, lambda;
LL g;
while(1) {
p = getPrime(L);
q = getPrime(L);
lambda = getLambda(p, q);
n = p * q;
if(__gcd(n, lambda) == 1 && p != q) break;
}
LL nn = 1LL * n * n;
getZnStar(ZnStar, n);
g = (1 + 1LL * n * getNumOfZnStar(ZnStar)) % nn;
return {PBK(n, g), SK(p, q, lambda)};
}
\(3.3\) 加密 \(m_{1},\ m_{2}\)
根據方案進行模擬,加密函數如下,\(qmul()\) 為快速乘算法,避免爆出 \(long\ long\) 范圍,\(qpow()\) 為快速冪算法,\(nn\) 為 \(n^2\)
LL encryption(int m, LL g, int n, vector<int>& ZnStar)
{
LL nn = 1LL * n * n;
LL r = getNumOfZnStar(ZnStar);
LL c = qmul(qpow(g, m, nn), qpow(r, n, nn), nn);
return c;
}
\(3.4\) 解密 \(m_{1},\ m_{2}\)
根據方案進行模擬,解密函數如下
LL getLx(LL x, int n, LL nn) { return (x - 1) / n % nn; }
LL decryption(LL c, LL g, int n, int lambda)
{
LL nn = 1LL * n * n;
LL x1 = qpow(c, lambda, nn), L1 = getLx(x1, n, nn);
LL x2 = qpow(g, lambda, nn), L2 = getLx(x2, n, nn);
LL mu = getInv(L2, n);
LL plaintext = L1 * mu % n;
return plaintext;
}
\(3.5\) 驗證同態
判斷 \(m_{1} + m_{2} = Dec(m_{1}\cdot m_{2})\) 即可
\(4.\) 代碼
#include <bits/stdc++.h>
#define LL long long
#define pb push_back
using namespace std;
struct PBK
{
int n;
LL g;
PBK() {}
PBK(int _n, LL _g): n(_n), g(_g) {}
void out() { cout << "公鑰 <n, g>: <" << n << ", " << g << ">" << endl; }
};
struct SK
{
int p, q, lambda;
SK() {}
SK(int _p, int _q, int _lambda): p(_p), q(_q), lambda(_lambda) {}
void out() { cout << "私鑰 <p, q, lambda>: <" << p << ", " << q << ", " << lambda << ">" << endl; }
};
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, MOD);
}
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(vector<int>& ZnStar, int n)
{
for(int i = 1; i < n; ++i)
if(__gcd(i, n) == 1)
ZnStar.pb(i);
}
int getNumOfZnStar(vector<int>& ZnStar) { return ZnStar[rand() % ZnStar.size()]; }
int getLambda(int p, int q) { return (p - 1) * (q - 1) / __gcd(p - 1, q - 1); }
pair<PBK, SK> getPublicKey(int L, vector<int>& ZnStar)
{
int n, p, q, lambda;
LL g;
while(1) {
p = getPrime(L);
q = getPrime(L);
lambda = getLambda(p, q);
n = p * q;
if(__gcd(n, lambda) == 1 && p != q) break;
}
LL nn = 1LL * n * n;
getZnStar(ZnStar, n);
g = (1 + 1LL * n * getNumOfZnStar(ZnStar)) % nn;
return {PBK(n, g), SK(p, q, lambda)};
}
LL encryption(int m, LL g, int n, vector<int>& ZnStar)
{
LL nn = 1LL * n * n;
LL r = getNumOfZnStar(ZnStar);
LL c = qmul(qpow(g, m, nn), qpow(r, n, nn), nn);
return c;
}
LL getLx(LL x, int n, LL nn) { return (x - 1) / n % nn; }
LL decryption(LL c, LL g, int n, int lambda)
{
LL nn = 1LL * n * n;
LL x1 = qpow(c, lambda, nn), L1 = getLx(x1, n, nn);
LL x2 = qpow(g, lambda, nn), L2 = getLx(x2, n, nn);
LL mu = getInv(L2, n);
LL plaintext = L1 * mu % n;
return plaintext;
}
int main()
{
srand((unsigned)time(NULL));
int L = 10, m1 = 15, m2 = 20;
vector<int> ZnStar;
auto i = getPublicKey(L, ZnStar);
PBK pbk = i.first;
SK sk = i.second;
pbk.out();
sk.out();
LL ciphertext1 = encryption(m1, pbk.g, pbk.n, ZnStar);
LL ciphertext2 = encryption(m2, pbk.g, pbk.n, ZnStar);
cout << "明文 m1 加密為: " << ciphertext1 << endl;
cout << "明文 m2 加密為: " << ciphertext2 << endl;
LL plaintext1 = decryption(ciphertext1, pbk.g, pbk.n, sk.lambda);
LL plaintext2 = decryption(ciphertext2, pbk.g, pbk.n, sk.lambda);
LL nn = 1LL * pbk.n * pbk.n;
LL ciphertext = qmul(ciphertext1, ciphertext2, nn);
LL plaintext = decryption(ciphertext, pbk.g, pbk.n, sk.lambda);
cout << "秘文 c1 解密為: " << plaintext1 << endl;
cout << "秘文 c2 解密為: " << plaintext2 << endl;
cout << "秘文 c1c2 解密為: " << plaintext << endl;
if(plaintext1 + plaintext2 == plaintext) cout << "滿足同態" << endl;
return 0;
}
