密碼學硬核筆記——特殊離散對數問題

最喜歡的lora

前言

前幾天參加了網鼎杯的比賽,第一道題就是簡單粗暴的離散對數問題(DLP)。當時自己是用sagemath的discrete_log()函數的直接秒解的(另外第三題必須要用cmd打開flag纔出來就很坑)。賽後發現這道離散對數題應該是用Pohlig–Hellman algorithm 做的,所以針對這次的題目學習了一遍該算法。


  • 定義6.7:設HH是羣GG的一個子集合,如果對於羣GG的二元運算,HH也構成一個羣,就稱HH爲羣GG子羣,記作HGH ≤ G
  • 定義6.8: 若羣GG包含的元素個數有限,則稱GG有限羣,否則稱爲無限羣。有限羣GG所包含的元素個數稱爲GG,記爲G| G |
  • 定義6.9 :設aa是羣GG中的一個元素,若存在正整數nn使得an=ea^n = e,則稱aa爲有限階元素,滿足an=ea^n = e的最小正整數nn叫做aa,記爲a| a |。若不存在正整數nn使得an=ea^n = e,則稱aa爲無限階元素。
  • 定義6.10 設GG爲羣,若存在GG的一個元素aa,使得GG中的任意元素均由aa的冪組成,即
    G={annZ}G = \{ a^n | n ∈ Z\}
    則稱羣GG循環羣,元素aa爲循環羣GG的生成元,記爲G=<a>G = < a >

後面的內容都是圍繞循環羣的子羣以及羣和元素的階展開的。


Pohlig–Hellman algorithm

維基百科解釋如下:is a special-purpose algorithm for computing discrete logarithms in a finite abelian group whose order is a smooth integer.
是一種特殊算法,在階爲光滑數的有限阿貝爾羣上解決離散對數問題。

同時

先看普遍情況下的算法:
在這裏插入圖片描述

大致流程如下:
(假設階爲ϕ(m)\phi(m))

首先,要解決的問題是
gxh mod m and n=ϕ(m)=i=1rpieig^x\equiv h \ mod\ m\ and\ n=\phi(m)=\prod_{i=1}^r p_i ^ {e_i}

等式兩邊同乘上一個指數npiei\frac{n}{p_i ^ {e_i}},可以得到:
{gx1)np1e1hnp1e1 mod mgx2)np2e2hnp2e2 mod mgx3)np3e3hnp3e3 mod m...gxr)nprerhnprer mod m\begin{cases} (g^{x_1})^\frac{n}{p_1 ^ {e_1}}\equiv h^\frac{n}{p_1 ^ {e_1}} \ mod\ m\\ (g^{x_2})^\frac{n}{p_2 ^ {e_2}}\equiv h^\frac{n}{p_2 ^ {e_2}} \ mod\ m\\ (g^{x_3})^\frac{n}{p_3 ^ {e_3}}\equiv h^\frac{n}{p_3 ^ {e_3}} \ mod\ m\\ ...\\ (g^{x_r})^\frac{n}{p_r ^ {e_r}}\equiv h^\frac{n}{p_r ^ {e_r}} \ mod\ m\\ \end{cases}

對於每一條式子來說,通過遍歷xi{0,1,...,piei}x_i\in\{0,1,...,p_i ^ {e_i}\}一定可以計算出一個xix_i滿足:gxi)npieihnpiei mod m(g^{x_i})^\frac{n}{p_i ^ {e_i}}\equiv h^\frac{n}{p_i ^ {e_i}} \ mod\ m

所以我們可以得到:

{xx1 mod  p1e1xx2 mod  p2e2xx3 mod  p3e3...xxr mod  prer\begin{cases} x\equiv x_1 \ \mod p_1 ^ {e_1}\\ x\equiv x_2 \ \mod p_2 ^ {e_2}\\ x\equiv x_3 \ \mod p_3 ^ {e_3}\\ ...\\ x\equiv x_r \ \mod p_r ^ {e_r}\\ \end{cases}

最後用中國剩餘定理可以算出最後的xx
xxmod  i=1rpieix \equiv x' \mod \prod_{i=1}^r p_i ^ {e_i}


理論依據

(爲什麼能算出這個xix_i?)

我們用羣理論重新梳理一遍整個算法。

根據題目內容,我們可以構建生成元爲gg ,階爲ϕ(m)\phi(m)(用n表示),模mm的有限循環羣
羣內元素爲:
G={1,g1,g2,...,gn}G=\{1,g^1,g^2,...,g^n\}
(這裏先講一下,循環羣的階與生成元的階是一樣的,暫且認爲階爲nn,因爲根據歐拉定理 gϕ(m)1mod  mg^{\phi(m)}\equiv1 \mod m,即使存在元素的階t小於nn(即gt1mod  mg^{t}\equiv1 \mod m),那也是nn的因子,可以通過遍歷因子的方式得到tt

通過這個循環羣我們可以知道hh是在該羣中的,而且x{0,1,..,n1}x\in\{0,1,..,n-1\}

那爲什麼要乘上指數呢?
其實對每個式子乘上指數是爲了構造多個不同的該循環羣的子羣。

gi=gnpieig_i=g^\frac{n}{pi^{e_i}},可以構造一個gig_i爲生成元,階爲pieip_i^{e_i}的循環羣子羣
元素爲
<gi>={1,gnpiei,gnpiei2,gnpiei3,...,gnpiei(piei1)}<g_i>=\{1,g^\frac{n}{pi^{e_i}},g^{\frac{n}{pi^{e_i}}*2},g^{\frac{n}{pi^{e_i}}*3},...,g^{\frac{n}{pi^{e_i}}*(p_i^{e_i}-1)}\}

同時我們也構造hi=hnpiei=(gx)npieimod  mh_i=h^{\frac{n}{pi^{e_i}}}=(g^{x})^{\frac{n}{pi^{e_i}}} \mod m(取模後xx變成了xix_i
因此hi<gi>h_i\in <g_i>,xix_i必定存在,我們可以通過遍歷xi[0,piei1]x_i\in [0,p_i^{e_i}-1]找到它。

同時我們要關注gxh mod m(gnpiei)xihnpiei mod mg^x\equiv h \ mod\ m\rightarrow (g^{\frac{n}{p_i^{e_i}}})^{x_i}\equiv h^\frac{n}{p_i ^ {e_i}} \ mod\ m
實際上是
x[0,n1]xi[0,piei1]x\in[0,n-1]\rightarrow x_i\in [0,p_i^{e_i}-1]
也就是說,每個等式計算出來的xix_i是原來的xx在不同模下的結果。所以我們才能用CRT把原來的xx算出來。


特殊算法(當階爲素數冪prime power):
在這裏插入圖片描述
其實就是普遍算法,爲了美觀代碼簡化了一點,差異不大。但可以與BSGSBSGS算法並用(其實我並不太清楚怎麼並用), 時間複雜度降爲O(ep)O(e\sqrt{p})


實戰演練

you_rise_me_up.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Crypto.Util.number import *
import random

n = 2 ** 512
m = random.randint(2, n-1) | 1
c = pow(m, bytes_to_long(flag), n)
print 'm = ' + str(m)
print 'c = ' + str(c)

# m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
# c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499

離散對數問題,nn25122^{512},素數冪,可以使用Pohlig–Hellman algorithm。

首先找階,因爲生成元m的階整除ϕ(n)\phi(n),所以我們可以通過遍歷22的冪來找到這個階。
遍歷之後發現階爲25102^{510},直接使用上述特殊算法就可得到結果。

(自己寫的辣雞代碼)

def Prime_power_Pohlig_Hellman(g,h,n,p,e):
    '''
    phi(n)=p^e
    g^(p^e)=1 mod n
    g^x=h mod n
    '''
    for a in range(e):
        if(pow(g,2**a,n)==1):
            break
    x_k=0
    e=a
    gm=pow(g,pow(p,e-1,n),n)
    print gm
    if(pow(g,p**e,n)!=1):
        print 'error'
    for k in range(e):
        _x=pow(p,e,n)-x_k
        if(pow(g,x_k,n)*pow(g,_x,n)%n !=1 ):
            print 'error'
        h_k=pow(pow(g,_x,n)*h,pow(p,e-1-k,n),n)
        for d_k in range(p):
            if(pow(gm,d_k,n)==h_k):
                print k,d_k
                break

        x_k=x_k+d_k*pow(p,k,n)
        
    return x_k  

if __name__ =='__main__':
    n=2**512
    m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
    c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499
    print libnum.n2s(Prime_power_Pohlig_Hellman(m, c, 2**512, 2, 511) )   

最後得到
在這裏插入圖片描述


後記

這篇博客的內容是我第一次使用新學的羣理論來分析(盡力了),可能有講得不好的地方,請多多包涵。

個人體會有兩個,一是其實算法並沒有那麼難看懂,英語也沒有那麼難讀懂,莫名的恐懼只是因爲不瞭解。所以多看幾遍就好了。

二是我覺得了解算法就是一種說服自己的過程,這個算法爲什麼要這樣做?理論依據又是什麼?學習算法的過程中,我一直在尋找理由說服自己,這個算法這樣做肯定是有它的理由的。可能有點吹毛求疵的感覺,但我只是想純粹的去理解算法。

三是自己還有很大的進步空間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章