1. 程式人生 > 實用技巧 >BZOJ-4522 [Cqoi2016]金鑰破解(Pollard-Rho演算法+exgcd)

BZOJ-4522 [Cqoi2016]金鑰破解(Pollard-Rho演算法+exgcd)

題目描述

  一種非對稱加密演算法的金鑰生成過程如下:

  \(1.\) 任選兩個不同的質數 \(p,q\)

  \(2.\) 計算 \(N=pq,r=(p-1)(q-1)\)

  \(3.\) 選取小於 \(r\) ,且與 \(r\) 互質的整數 \(e\)

  \(4.\) 計算整數 \(d\),使得 \(ed\equiv 1 \pmod {r}\)

  \(5.\) 二元組 \((N,e)\) 稱為公鑰,二元組 \((N,d)\) 稱為私鑰。

  當需要加密訊息 \(n\) 時(假設 \(n\) 是一個小於 N 的整數,因為任何格式的訊息都可轉為整數表示),使用公鑰 \((N,e)\)

,按照 \(n^e\equiv c \pmod {N}\) 運算,可得到密文 \(c\)

  對密文 \(c\) 解密時,用私鑰 \((N,d)\),按照 \(c^d\equiv n \pmod {N}\) 運算,可得到原文 \(n\)。演算法正確性證明省略。

  由於用公鑰加密的密文僅能用對應的私鑰解密,而不能用公鑰解密,因此稱為非對稱加密演算法。通常情況下,公鑰由訊息的接收方公開,而私鑰由訊息的接收方自己持有。這樣任何傳送訊息的人都可以用公鑰對訊息加密,而只有訊息的接收方自己能夠解密訊息。

  現在,你的任務是尋找一種可行的方法來破解這種加密演算法,即根據公鑰破解出私鑰,並據此解密密文。

  輸入:\(e,N,c(N\leq 2^{62},c<N)\),輸出:\(d,n\)

分析

  \(1.\) \(\text{Pollard-Rho}\) 分解 \(N\)

  $2. $ 取 \(N\) 兩個質因數 $p,q $,得到 \(r\)

  \(3.\) 利用擴充套件歐幾里得演算法求出 \(e\) 的逆元 \(d\)

  \(4.\) \(c,d\) 都已知,快速冪算 \(n\)

程式碼

#include<bits/stdc++.h>
using namespace std;
#define int128 __int128
long long e,N,c,r,d,n;
long long quick_pow(long long a,long long b,long long mod)
{
    long long ans=1;
    while(b)
    {
        if(b&1)
            ans=(int128)ans*a%mod;
        a=(int128)a*a%mod;
        b>>=1;
    }
    return ans;
}
long long max_factor;
bool Miller_Rabin(long long p)
{
    if(p<2)
        return 0;
    if(p==2)
        return 1;
    if(p==3)
        return 1;
    long long d=p-1,r=0;
    while(d%2==0)
    {
        r++;
        d>>=1;
    }
    for(long long k=0;k<10;k++)
    {
        long long a=rand()%(p-2)+2;
        long long x=quick_pow(a,d,p);
        if(x==1||x==p-1)
            continue;
        for(int i=0;i<r-1;i++)
        {
            x=(int128)x*x%p;
            if(x==p-1)
                break;
        }
        if(x!=p-1)
            return 0;
    }
    return 1;
}
long long f(long long x,long long c,long long n)
{
    return ((int128)x*x+c)%n;
}
long long Pollard_Rho(long long n)
{
    long long s=0,t=0;
    long long c=rand()%(n-1)+1;
    int step=0,goal=1;
    long long val=1;
    for(goal=1; ;goal<<=1,s=t,val=1)
    {
        for(step=1;step<=goal;step++)
        {
            t=f(t,c,n);
            val=(int128)val*abs(t-s)%n;
            if(step%127==0)
            {
                long long d=__gcd(val,n);
                if(d>1)
                    return d;
            }
        }
        long long d=__gcd(val,n);
        if(d>1)
            return d;
    }
}
vector<long long> factor;
void fac(long long x)
{
    if(x<=max_factor||x<2)
        return ;
    if(Miller_Rabin(x))
    {
        factor.push_back(x);
        //max_factor=max(max_factor,x);
        return ;
    }
    long long p=x;
    while(p>=x)
        p=Pollard_Rho(x);
    while(x%p==0)
        x=x/p;
    fac(x);fac(p);
}
void exgcd(long long a,long long b,long long &x,long long &y)
{
    if(!b)
    {
        x=1;
        y=0;
        return ;
    }
    exgcd(b,a%b,y,x);
    y=y-(int128)(a/b)*x%r;
    y=(y%r+r)%r;
}
long long inv(long long a,long long b)//ax=1(mod b)
{
    long long x,y;
    exgcd(a,b,x,y);
    x=(x%b+b)%b;
    return x;
}
int main()
{
    srand((unsigned)time(NULL));
    cin>>e>>N>>c;
    fac(N);
    r=(factor[0]-1)*(factor[1]-1);
    d=inv(e,r);
    n=quick_pow(c,d,N);
    cout<<d<<" "<<n<<endl;
    return 0;
}