【题解】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 小时前
【动态规划系列】路径问题、子数组 和 子序列问题、背包问题
c++·算法·动态规划
aqiu1111111 小时前
【数位 DP / 记忆化搜索】蓝桥云课 - 光轨区的 AI 数 题解
c++·算法·蓝桥杯·数位dp
地平线开发者2 小时前
Horizon J6m 部署 YOLOv8s INT8 精度下降问题排查记录
算法
橘子汽水1682 小时前
Leetcode 200,994岛屿数量,腐烂的橘子
数据结构·算法·leetcode
欧特克_Glodon2 小时前
OpenCV计算机视觉开发入门与实践<三十七>:OpenCV中的机器学习
c++·opencv·机器学习·计算机视觉
keven-wang3 小时前
密码学-6个密码学核心概念
密码学
老赵的博客3 小时前
c++ 之观察者模式
c++·qt
坚定学代码3 小时前
static在c++中的使用
开发语言·c++
hansang_IR3 小时前
【题解】P5364 [SNOI2017] 礼物
c++·线性代数·算法