1. 程式人生 > >破解 D-H 協議(永遠不可能學會的數論之BSGS演算法)

破解 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^{x}\equiv b\%p      a,b,p都一直,且p為素數,求解x

令 m = sqrt(p) + 1 ( 稍大於sqrt(p)的一個整數)

令 x = i*m - j ,ij均為整數,那麼i*m-j一定可以湊出任意一個整數x,並且i只需從1到m,如果有誤差,可以通過 j 來調整。

這樣一來,同餘方程變為a^{i*m}\equiv b*a^{j}\%p

那麼恆等式右邊,可以列舉 j 預處理放到map裡,然後左邊列舉 i ,直到在map存在相等的右邊值,則結束

解得:x = i*m - j

時間複雜度O(\sqrt{p}logp),當p很大時(1e9),還是要考慮優化,一不小心就超時

所以本題目已知     g^{b}\equiv A\%p   利用bsgs求解b,所以答案K=A^{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);

    }

}