破解 D-H 協議(永遠不可能學會的數論之BSGS演算法)
時間限制: 1 Sec 記憶體限制: 128 MB
提交: 133 解決: 53
[提交] [狀態] [討論版] [命題人:admin]
題目描述
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。
題意 :
給你A B g p ,讓你去求K k=A^b%p 或者k=B^a%p ,bsgs演算法解同餘方程 a^x % p = b % p
A=g^a%p 等價於 A%p=g^a%p 等價於A≡g^a(mod p) (事實) 就可以套用BSGS演算法了
(恆成立 一般是表示含有未知量的)
因為( aX=kb+c 等於 aX≡c(mod b) )。
題解
【bsgs演算法】
對於同餘方程 a,b,p都一直,且p為素數,求解x
令 m = sqrt(p) + 1 ( 稍大於sqrt(p)的一個整數)
令 x = i*m - j ,ij均為整數,那麼i*m-j一定可以湊出任意一個整數x,並且i只需從1到m,如果有誤差,可以通過 j 來調整。
這樣一來,同餘方程變為
那麼恆等式右邊,可以列舉 j 預處理放到map裡,然後左邊列舉 i ,直到在map存在相等的右邊值,則結束
解得:x = i*m - j
時間複雜度,當p很大時(1e9),還是要考慮優化,一不小心就超時
所以本題目已知 利用bsgs求解b,所以答案
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll qpow(ll n,ll m,ll p)
{
n%=p;
ll ans=1;
for(;m;m>>=1)
{
if(m&1)ans=ans*n%p;
n=n*n%p;
}
return ans;
}
int main()
{
ll g,p,A,B,n;
scanf("%lld%lld%lld",&g,&p,&n);
ll m=sqrt(p)+1; //x=i*m-j,注意m必須大於sqrt(p)
map<ll,ll>M; //預處理g^j
ll res=1; //a^0
for(int j=0;j<=m;j++)
{
M[res%p]=j;
res=res*g%p;
}
ll dk=qpow(g,m,p);
while(n--)
{
scanf("%lld%lld",&A,&B);
int a,b;
ll k=dk*qpow(B,p-2,p)%p;
for(int i=1;i<=m;i++)
{
if(M.count(k))
{
b=i*m-M[k];
break;
}
k=k*dk%p;
}
ll ans=qpow(A,b,p);
printf("%lld\n",ans);
}
}