P4454 CQOI2018 破解D-H协议 - 洛谷 (luogu.com.cn)
终于做到密码学相关的啦好开心!
正好复习一下 BSGS,学了这么多年的数论终于有用武之地!
这道题是板子,很简单,但我会拓展一些密码学、数论、群论相关内容,自行选读。
0.与解题无关
Diffie-Hellman(迪菲-赫尔曼密钥交换,简称 D-H)
是一种在不安全通信信道上,安全地交换加密密钥的密码学协议。
大致流程:
-
双方公开约定两个数字:一个大质数
和一个底数
(原根)。
-
Enigma 选一个秘密数
(私钥),计算
,把
(公钥)发给 Ulrich。
-
Ulrich 选一个秘密数
(私钥),计算
,把
(公钥)发给 Enigma。
-
Enigma 计算:
-
Ulrich 计算:
由于模幂运算的交换律,两者得出的 是完全相同的。
而外界黑客只知道 ,想要算出
,就必须解决离散对数难题。
在现有计算机能力下,对大质数极其难解,这就是 D-H 的安全性基石。
概念解释:
原根(Primitive Root),也叫本原元。
定义:对于一个质数
,如果存在一个整数
。
使得
的
次方、
次方、
次方......一直到
次方,
分别
![]()
后得到的余数,不重不漏地覆盖了
到
之间的所有整数。
那么
就是模
的一个原根。
初等数论定义:
设整数
与模数
互质,
阶 是指让
成立的最小正整数
,记作
。
在以上基础上,
的阶等于欧拉函数
,则称
是模
的原根。
写成公式:
其中
表示小于等于
且与
互质的正整数个数。
另:当
为质数,
。
群论定义:
是模
乘法群
的生成元,即:
这表示
的幂次能遍历所有与
互质的余数集合。
模幂运算的交换律:
离散对数:形如
。
因为
操作导致结果并非连续,而是在
的余数域上不规律跳动。
现有计算机与 D-H 的安全性:离散对数在数学结构上是一个没有逆向公式的单向函数,
且其破译时间复杂度随着数据变大而指数级增长。
但如果使用量子计算机,能在多项式时间内快速求解离散对数。
这也是为什么现在全球密码学界在大力推进**后量子密码学(PQC)**的原因。
1.题意解读
Diffie-Hellman 密钥交换过程中,公开的信息有:
-
固定公开 :质数
,原根
-
信道窃听得到 :
要破解的是最终密钥:
已知 ,但不知道私密的随机数
或
。
破解思路 :
只要算出其中一个私密数,比如 ,那么:
而 就是以
为底、
的离散对数:
所以,每组数据都是在解一个离散对数问题。
BSGS 就是专门用来求解 中
的算法。
它的时空复杂度都是 。
本题数据范围:,用 BSGS 轻松过。
PS:本人还是认为题意很有意思,有真的在破译密码的感觉。
虽然只是 BSGS 板子,某种意义上还有隐私安全教育环节。
2.代码
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
map<LL, LL> mp;
// a ^ x = b (mod p)
LL BSGS(LL a, LL b, LL p) {
a %= p; b %= p;
if (b == 1) { // 特判
return 0;
}
mp.clear();
mp[b] = 0; // 不走小步
LL m = ceil(sqrt(p));
LL am = a;
for (LL j = 1, t = b; j < m; j ++) {
t = t * a % p;
mp[t] = j;
am = am * a % p;
}
for (LL i = 1, t = 1; i <= m; i ++) {
t = t * am % p;
if (mp.count(t)) {
return i * m - mp[t];
}
}
return -1;
}
LL g, P;
LL q_pow(LL a, LL b) {
LL c = 1;
while (b) {
if (b & 1) {
c = c * a % P;
}
a = a * a % P;
b >>= 1;
}
return c;
}
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> g >> P;
int n;
cin >> n;
for (int i = 1; i <= n; i ++) {
LL A, B;
cin >> A >> B;
LL a = BSGS(g, A, P);
LL K = q_pow(B, a);
cout << K << "\n";
}
return 0;
}