前言
前幾天參加了網鼎杯的比賽,第一道題就是簡單粗暴的離散對數問題(DLP)。當時自己是用sagemath的discrete_log()函數的直接秒解的(另外第三題必須要用cmd打開flag纔出來就很坑)。賽後發現這道離散對數題應該是用Pohlig–Hellman algorithm 做的,所以針對這次的題目學習了一遍該算法。
羣
- 定義6.7:設是羣的一個子集合,如果對於羣的二元運算,也構成一個羣,就稱爲羣的子羣,記作。
- 定義6.8: 若羣包含的元素個數有限,則稱爲有限羣,否則稱爲無限羣。有限羣所包含的元素個數稱爲的階,記爲。
- 定義6.9 :設是羣中的一個元素,若存在正整數使得,則稱爲有限階元素,滿足的最小正整數叫做的階,記爲。若不存在正整數使得,則稱爲無限階元素。
- 定義6.10 設爲羣,若存在的一個元素,使得中的任意元素均由的冪組成,即
則稱羣爲循環羣,元素爲循環羣的生成元,記爲。
後面的內容都是圍繞循環羣的子羣以及羣和元素的階展開的。
Pohlig–Hellman algorithm
維基百科解釋如下:
是一種特殊算法,在階爲光滑數的有限阿貝爾羣上解決離散對數問題。
同時
先看普遍情況下的算法:
大致流程如下:
(假設階爲)
首先,要解決的問題是
等式兩邊同乘上一個指數,可以得到:
對於每一條式子來說,通過遍歷一定可以計算出一個滿足:
所以我們可以得到:
最後用中國剩餘定理可以算出最後的:
理論依據
(爲什麼能算出這個?)
我們用羣理論重新梳理一遍整個算法。
根據題目內容,我們可以構建生成元爲 ,階爲(用n表示),模的有限循環羣
羣內元素爲:
(這裏先講一下,循環羣的階與生成元的階是一樣的,暫且認爲階爲,因爲根據歐拉定理 ,即使存在元素的階t小於(即),那也是的因子,可以通過遍歷因子的方式得到)
通過這個循環羣我們可以知道是在該羣中的,而且
那爲什麼要乘上指數呢?
其實對每個式子乘上指數是爲了構造多個不同的該循環羣的子羣。
令,可以構造一個以爲生成元,階爲的循環羣子羣
元素爲
同時我們也構造(取模後變成了)
因此,必定存在,我們可以通過遍歷找到它。
同時我們要關注
實際上是
也就是說,每個等式計算出來的是原來的在不同模下的結果。所以我們才能用CRT把原來的算出來。
特殊算法(當階爲素數冪prime power):
其實就是普遍算法,爲了美觀代碼簡化了一點,差異不大。但可以與算法並用(其實我並不太清楚怎麼並用), 時間複雜度降爲。
實戰演練
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
離散對數問題,爲,素數冪,可以使用Pohlig–Hellman algorithm。
首先找階,因爲生成元m的階整除,所以我們可以通過遍歷的冪來找到這個階。
遍歷之後發現階爲,直接使用上述特殊算法就可得到結果。
(自己寫的辣雞代碼)
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) )
最後得到
後記
這篇博客的內容是我第一次使用新學的羣理論來分析(盡力了),可能有講得不好的地方,請多多包涵。
個人體會有兩個,一是其實算法並沒有那麼難看懂,英語也沒有那麼難讀懂,莫名的恐懼只是因爲不瞭解。所以多看幾遍就好了。
二是我覺得了解算法就是一種說服自己的過程,這個算法爲什麼要這樣做?理論依據又是什麼?學習算法的過程中,我一直在尋找理由說服自己,這個算法這樣做肯定是有它的理由的。可能有點吹毛求疵的感覺,但我只是想純粹的去理解算法。
三是自己還有很大的進步空間。