【题解】P4454 [CQOI2018] 破解D-H协议

P4454 CQOI2018 破解D-H协议 - 洛谷 (luogu.com.cn)

终于做到密码学相关的啦好开心!

正好复习一下 BSGS,学了这么多年的数论终于有用武之地!

这道题是板子,很简单,但我会拓展一些密码学、数论、群论相关内容,自行选读。


0.与解题无关

Diffie-Hellman(迪菲-赫尔曼密钥交换,简称 D-H)

是一种在不安全通信信道上,安全地交换加密密钥的密码学协议。

大致流程:

  1. 双方公开约定两个数字:一个大质数 和一个底数 (原根)。

  2. Enigma 选一个秘密数 (私钥),计算 ,把 (公钥)发给 Ulrich。

  3. Ulrich 选一个秘密数 (私钥),计算 ,把 (公钥)发给 Enigma。

  4. Enigma 计算:

  5. 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;
}
相关推荐
凯瑟琳.奥古斯特1 小时前
力扣1015C++解法技巧
c++·算法·leetcode·链表·职场和发展
seowmt1 小时前
GEO效果该什么时候看数据:从立项到复盘的时间线复盘
算法·ip·seo·geo·faq
悠仁さん1 小时前
【算法C++】双指针(一)
数据结构·c++·算法
jimy11 小时前
lvalue、xvalue、prvalue(二):引用是哪一类?
开发语言·c++·1024程序员节
小豆庄1 小时前
本地视频脱敏的追踪系统踩坑——车牌过滤、转场误判与ID冲突排查
人工智能·算法·目标检测·音视频·视频·敏捷流程·纹理贴图
忆挽篱笙歌1 小时前
进程替换函数与shenll命令行解释器
前端·javascript·算法
YukiRinLL1 小时前
字符串存储与 BF、KMP 匹配算法
数据结构·算法
小超人诞生日记1 小时前
Linux:环境变量 & 进程虚拟地址空间
linux·算法
杜 硕1 小时前
从零开始的C++生活——第三讲
开发语言·c++·生活
小白起 v1 小时前
【算法学习 · 01】深入理解大 O 表示法:从「能跑就行」到「专业评估」算法性能
算法