题目描述
Diffie-Hellman密钥交换协议是一种简单有效的密钥交换方法。它可以让通讯双方在没有事先约定密钥(密码)的情况下,通过不安全的信道(可能被窃听)建立一个安全的密钥K,用于加密之后的通讯内容。
假定通讯双方名为Alice和Bob,协议的工作过程描述如下(其中mod表示取模运算):
协议规定一个固定的质数P,以及模P的一个原根g。P和g的数值都是公开的,无需保密。
Alice生成一个随机数a,并计算A=ga mod P,将A通过不安全信道发送给Bob。
Bob生成一个随机数b,并计算B=gb mod P,将B通过不安全信道发送给Alice。
Bob根据收到的A计算出K=Ab mod P ,而Alice根据收到的B计算出K=Ba mod P。
双方得到了相同的K,即gab mod P。K可以用于之后通讯的加密密钥。
可见,这个过程中可能被窃听的只有A,B,而a,b,K是保密的。并且根据A,B,P,g这4个数,不能轻易计算出K,因此K可以作为一个安全的密钥。
当然安全是相对的,该协议的安全性取决于数值的大小,通常a,b,P都选取数百位以上的大整数以避免被破解。然而如果Alice和Bob编程时偷懒,为了避免实现大数运算,选择的数值都小于231,那么破解他们的密钥就比较容易了。
输入
第一行包含两个空格分开的正整数g和P。
第二行为一个正整数n,表示Alice和Bob共进行了n次连接(即运行了n次协议)。
接下来n行,每行包含两个空格分开的正整数A和B,表示某次连接中,被窃听的A,B数值。
输出
输出包含n行,每行一个正整数K,为每次连接你破解得到的密钥。
样例输入
3 31 3 27 16 21 3 9 26
样例输出
4 21 25
提示
对于30%的数据,2≤A,B,P≤1000。
对于100%的数据,2≤A,B<P<231,2≤g<20,1≤n≤20。
很裸的 baby_step_giant_step(bsgs)算法板子。
AC代码(其实不用这么麻烦,但是为了记住这个板子用了exbsgs)
/* Lyl */
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3fLL
#define pi acos(-1.0)
#define ms(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
ll qpow(ll x, ll y, ll mod){ll s=1;while(y){if(y&1)s=s*x%mod;x=x*x%mod;y>>=1;}return s;}
int baby_step_giant_step(int a, int b, int p)
{
a %= p, b %= p;
if(b == 1) return 0;
int cnt = 0;
ll t = 1;
for(int g = __gcd(a, p); g != 1; g = __gcd(a, p))
{
if(b % g) return -1;
p /= g, b /= g, t = t * a / g % p;
++cnt;
if(b == t) return cnt;
}
map<int, int> hash;
int m = int(sqrt(1.0 * p) + 1);
ll base = b;
for(int i = 0; i != m; ++i)
{
hash[base] = i;
base = base * a % p;
}
base = qpow(a, m, p);
ll now = t;
for(int i = 1; i <= m + 1; ++i)
{
now = now * base % p;
if(hash.count(now))
return i * m - hash[now] + cnt;
}
return -1;
}
int main()
{
ll g, a, b, p, n;
scanf("%lld%lld%lld", &g, &p, &n);
while(n--)
{
scanf("%lld%lld", &a, &b);
ll ans=baby_step_giant_step(g, a, p);
if(ans==-1) continue;
printf("%lld\n", qpow(b, ans, p));
}
return 0;
}