Diffie-Hellman密钥交换协议是一种简单有效的密钥交换方法。它可以让通讯双方在没有事先约定密钥(密码)的情况下通过不安全的信道(可能被窃听)建立一个安全的密钥K,用于加密之后的通讯内容。
假定通讯双方名为Alice和Bob,协议的工作过程描述如下(其中mod表示取模运算):
1.协议规定一个固定的质数P,以及模P的一个原根g。P和g的数值都是公开的,无需保密。
2.Alice生成一个随机数a,并计算A=g^a mod P,将A通过不安全信道发送给Bob。
3.Bob生成一个随机数b,并计算B=g^b mod P,将B通过不安全信道发送给Alice。
4.Bob根据收到的A计算出K=A^b mod P,而Alice根据收到的B计算出K=B^a mod P。
5.双方得到了相同的K,即g^(a*b) mod P。K可以用于之后通讯的加密密钥。
可见,这个过程中可能被窃听的只有A、B,而a、b、K是保密的。并且根据A、B、P、g这4个数,不能轻易计算出K,因此K可以作为一个安全的密钥。
当然安全是相对的,该协议的安全性取决于数值的大小,通常a、b、P都选取数百位以上的大整数以避免被破解。然而如果Alice和Bob编程时偷懒,为了避免实现大数运算,选择的数值都小于2^31,那么破解他们的密钥就比较容易了。
输入文件第一行包含两个空格分开的正整数g和P。
第二行为一个正整数n,表示Alice和Bob共进行了n次连接(即运行了n次协议)。
接下来n行,每行包含两个空格分开的正整数A和B,表示某次连接中,被窃听的A、B数值。
2≤A,B<P<231,2≤g<20,n<=20 2 ≤ A , B < P < 2 31 , 2 ≤ g < 20 , n <= 20
输出包含n行,每行1个正整数K,为每次连接你破解得到的密钥。
3 31
3
27 16
21 3
9 26
4
21
25
这题就是裸的BSGS模板题,对于 A≡ga A ≡ g a (mod p),我们可以用BSGS来求出a,从而直接求 Ba B a 就行了。接下来来讲讲BSGS是怎么使用的。
BSGS定义
BSGS,就是平常所说的大步小步算法,这个算法可以较高效地求出形如同余方程 Ax≡B A x ≡ B (mod C)(C是质数)的解。
如何实现
我们可以使用分块来优化暴力枚举,对于 Ax≡B A x ≡ B (mod C),我们可以令 m=⌈C−−√⌉ m = ⌈ C ⌉ , x=i∗m+j x = i ∗ m + j ,那么该式子就可以写成 Ai∗m∗Aj≡B A i ∗ m ∗ A j ≡ B (mod C),于是我们可以将 Aj A j 进行预处理,表示达到这个值的指数项为多少,存到一个哈希表或者map里,之后我们只要枚举 Ai∗m A i ∗ m 就行了,大致复杂度是 O(n−−√logn) O ( n l o g n ) 。(知道了这个,这题就做完了qwq)
#pragma GCC optimize(3)
#include
#include
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
mapint >num;
ll g,a,b,p,A,B;
int T,n;
inline void exgcd(ll a,ll b,ll &d,ll &x,ll &y) {
if(!b)
d=a,x=1,y=0;
else
exgcd(b,a%b,d,y,x),y-=x*(a/b);
}
inline ll inv(ll x) {
ll d,xx,y;
exgcd(x,p,d,xx,y);
return d==1?(xx+p)%p:-1;
}
inline ll pw(ll a,ll b) {
ll res=1;
while(b) {
if(b&1)
res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
inline ll solve(ll x) {
for(int i=0;i<=n;i++) {
ll v=x*inv(pw(g,i*n))%p;
if(num.count(v))
return num[v]+i*n;
}
return -1;
}
int main() {
read(g),read(p),read(T);
n=ceil(sqrt(p));
ll x=1;
num[1]=0;
for(int i=1;i<=n;i++) {
x=x*g%p;
if(!num[x])
num[x]=i;
}
while(T--) {
read(A),read(B);
printf("%lld\n",pw(A,solve(B)));
}
}