密码学——离散对数问题(DLP)
1840482075305597 发表于 江西 CTF 3757浏览 · 2024-02-28 07:01

DLP (Discrete Logarithm Problem)

在密码学中,离散对数问题是指在一个离散对数群中寻找指定的基值 g、模数 p 和给定结果 y 时,解方程

其中 x 为未知数,也是要求解的数。

生成元

生成元是数论中的一个概念,也被称为本原元

给定一个正整数 m,如果存在一个整数 g,使得集合 {g^0, g^1, g^2, ..., g^(m-1)} 中的每个元素对模 m 都不相同,则 g 被称为模 m 的生成元。

设 G 是一个群,如果存在一个元素 g ∈ G,通过对 g 进行重复的群运算,可以得到 G 中的每个元素,那么 g 被称为 G 的生成元。这意味着对于任意元素 a ∈ G,存在整数 n,使得 g^n = a在模 p 的剩余类中。

举个例子:若p是素数,计算

其中 x 取遍 1 到 p-1 的所有值时,都会得到不同的结果,那么g为p的生成元

在一个循环群中,生成元的数量等于群中元素的数量

阶(Order)是指元素在某个运算下的最小重复次数
对于群来说,阶指的是群中某个元素进行群运算后得到单位元所需的最小次数,也可以说是指数

指数,原根

设m>1是整数,a是与m互素的正整数,则使得

成立的最小正整数e叫做a对模m的指数,记作:

如果a对模m 的指数是φ(m),则a叫做模m的原根

根据欧拉定理,当a和m互质时,有a^φ(m) ≡ 1 (mod m)。这意味着a的阶(即最小的正整数d,使得a^d ≡ 1 (mod m))必须是φ(m)的因数。

discrete_log(y,g)

常用的求离散对数的方法(利用Pohlig-Hellman--求解光滑阶循环群上的离散对数算法)

  • 要求

中g必须是原根

  • 要求阶是光滑的(阶可拆成多个素数相乘,素数p的阶为p-1)

并没有范围要求

题一:

NEEPUSec CTF 2023 easymath

N = 7389313481223384214994762619823300589978423075857540712007981373887018860174846208000957230283669342186460652521580595183523706412588695116906905718440770776239313669678685198683933547601793742596023475603667
m = 1391372358062724416224243990838035874507346098208831800403257
c = 2275123956203765363233765961297507379124236980967603145079239177208264523438555378298177602517599708533004805369986246098127863480798528206818576888655427591685534478161571717613561380234338516607855593046949
fac = [2, 136327, 169937, 313351, 321427, 323377,356887, 413783, 519733,792413, 860077, 906289, 976501]
G=Zmod(N)
m1=G(m)
c1=G(c)
oo=[]
for i in fac:
    h=(N-1)//i 
    # 乘不乘次方没问题,毕竟最后都会被整除掉
    dlp1=discrete_log(c1**h,m1**h)
    # 换域了
    oo.append(int(dlp1))
sk=crt(oo,fac) 
mod = prod(fac) # 所有fac的乘积
for i in range(100):
    print(long_to_bytes(int(sk + i * mod)))
# Neepu{Nsmoothnumber}

其实,这题也可直接转:

m = 1391372358062724416224243990838035874507346098208831800403257
a=2275123956203765363233765961297507379124236980967603145079239177208264523438555378298177602517599708533004805369986246098127863480798528206818576888655427591685534478161571717613561380234338516607855593046949
G=Zmod(N) 
m1=G(m) 
c1=G(a) 
print(long_to_bytes(ZZ(discrete_log(c1,m1))))
# Neepu{Nsmoothnumber}

里面就是下面这个算法,最后就是自动换域了,换成了上面题目中的i

算出m个关于x的式子后,来一发CRT即可(不需要扩展CRT,因为模数互质)。

“注意!CRT方程组必定有解,因此,如果原根变换后的最后的扩展gcd无解,也说明原方程无解!!!”

针对Pohlig-Hellman,给出以下例题

题二

题目描述:

import hashlib
from Crypto.Util.number import *
secret = getPrime(128)
m = getPrime(128)
n = getPrime(1024)
c = pow(m, secret, n)
flag = "flag{"+hashlib.md5(str(secret).encode()).hexdigest()+"}"
print("m = %d" % m)
print("n = %d" % n)
print("c = %d" % c)
# m = 211060723077960779250044744266141176829
# n = 89884656743115795386465259539451236680898848947115328636715040578866337902750481566354238661203768010560056939935696678829394884407208311246423715319737062188883946712432742638151109800623047059726541476042502884419075341171231440736956555270413618581675255342293149119974022969989259739901207135911877936187
# c = 32977205552939587326066964781587345932834856116807593874246357335611184723568494377764636367075033866504466671442418777279691941123340914970584204650416137940499348297233941269700668358058974528742129237158207741614517093787264725738282484698794547919205696467463725584733972956537910246421504887816202122161

题目分析:

n是1024bits的素数,过大而无法直接调用sage求离散对数

尝试分解n-1得到:

n = 89884656743115795386465259539451236680898848947115328636715040578866337902750481566354238661203768010560056939935696678829394884407208311246423715319737062188883946712432742638151109800623047059726541476042502884419075341171231440736956555270413618581675255342293149119974022969989259739901207135911877936187
factor(n-1)
# 2 * 659 * 144811523 * 158091863 * 167642023 * 194814973 * 198114301509256817 * 1524198318315919100927 * 302061228405984819868188635839278249994068296319393442016901959084878254985929326557136330675603997640265462782948042782543029960066166632904951616712643712462381886167331227465971149482608610738439655060064241698902750467248492676743

利用Pohlig-Hellman的思路,有以下:

import hashlib
"""
n = 89884656743115795386465259539451236680898848947115328636715040578866337902750481566354238661203768010560056939935696678829394884407208311246423715319737062188883946712432742638151109800623047059726541476042502884419075341171231440736956555270413618581675255342293149119974022969989259739901207135911877936187
factor(n-1)
# 2 * 659 * 144811523 * 158091863 * 167642023 * 194814973 * 198114301509256817 * 1524198318315919100927 * 302061228405984819868188635839278249994068296319393442016901959084878254985929326557136330675603997640265462782948042782543029960066166632904951616712643712462381886167331227465971149482608610738439655060064241698902750467248492676743
"""
# c = pow(m, secret, n)
# h = g^x mod p
def r(h, g, N, p, qi):
    """
    N : p-1
    qi: N中的素因子
    """

    Zp = Zmod(p)
    h = pow(h, N//qi, p)
    g = pow(g, N//qi, p)
    ri = discrete_log(Zp(h), Zp(g))
    return int(ri)
m = 211060723077960779250044744266141176829
n = 89884656743115795386465259539451236680898848947115328636715040578866337902750481566354238661203768010560056939935696678829394884407208311246423715319737062188883946712432742638151109800623047059726541476042502884419075341171231440736956555270413618581675255342293149119974022969989259739901207135911877936187
c = 32977205552939587326066964781587345932834856116807593874246357335611184723568494377764636367075033866504466671442418777279691941123340914970584204650416137940499348297233941269700668358058974528742129237158207741614517093787264725738282484698794547919205696467463725584733972956537910246421504887816202122161
tmp_list = [659, 144811523, 158091863, 167642023, 194814973]
r_list = []
for qi in tmp_list:
    tmp = r(c,m,n-1,n,qi)
    print(tmp)
    r_list.append(tmp)
x = crt(r_list, tmp_list)

module = 1
for i in tmp_list:
    module *= i  
while True:
    if int(x).bit_length()>128:
        print('fail')
        break
    if int(pow(m, x, n))==c:
        print('x =', x)
        flag = "flag{"+hashlib.md5(str(x).encode()).hexdigest()+"}"
        print(flag)
        break
    x += module
# crt结果:425215994037823111210955984648839730 nbits() = 119
# x = 256148714020855512578941590011688772421
# flag{bbb04cf5180893e23e559c63ceae5b8f}

上面的知识理解了这题也就能理解了,照着解题思路求解下面一道类似的题:

题三

题目描述:

from Crypto.Util.number import *
import hashlib


class D_H():
    def __init__(self):
        self.a = getPrime(128)
        self.b = getPrime(128)
        self.p = getPrime(1024)
        self.g = getPrime(128)
        print("p =", oct(self.p))
        print('g =', oct(self.g))

    def Alice(self):
        A = pow(self.g, self.a, self.p)
        print('A =', oct(A))

    def Bob(self):
        B = pow(self.g, self.b, self.p)
        print('B =', oct(B))

    def key_exchange(self):
        key = pow(self.g, self.a * self.b, self.p)
        return key


DH = D_H()
DH.Alice()
DH.Bob()
key = DH.key_exchange()
flag = "flag{" + hashlib.md5(str(key).encode()).hexdigest() + "}"

"""
p = 0o100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002320461720107654672567257101774542402073
g = 0o2065141432751712040220440246142105771015421
A = 0o43613040531475331260645277703363154024375552447624145423154467212453566671627031247565752140361214442354760036367561323225723114644705564775357161722423055427767542147531265612413043244400626062610066133355056455656354327315127131604056020315605217552254443755433425633455524145337447120342451441170743174123742307207301542043164563340422677
B = 0o60346662663461540211665020605034076206107430140346645020415164214412402345454104341770532135130621505726240703066624656170403450176106112505427262227076032102233051024443206700250621734407223434463067213634725130117240556410501066411312272344517322556714720077305705512606154144764640154454705523667767514601216365471777467432753500477011706
"""

题目分析:

求解出a,b,才能求出k,那我们来求求a,b
a,b就照上面的思路求解即可

import hashlib
p = 89884656743115795386465259539451236680898848947115328636715040578866337902750481566354238661203768010560056939935696678829394884407208311246423715319737062188883946712432742638151109800623047059726541476042502884419075341171231440736956555270413618581675255342293149119974022969989259739901207135911877936187
g = 178978976577444006672923576665226091281
A = 50239356217905865472590693130535124452580643255845661649114913837176616545680446441660877442220510431707262797664254080937029365543267241063727348540325656758225475328002443452916853398111285325871347982839528070974557886108688133202930757479488756682939702883037420270843439335589319393168138956304604603839
B = 68046726556991975149664595590016707595227397021142933235637385938062726185554085368749325804969213299585765894674681296185961941854759720420418514055458989818389672323352887448970349935846465613203461132495952672474425884078444741734977925886709761740257008768019155974481354455945984504687605522607787217862
tmp_list = [2 , 659 , 144811523 , 158091863 , 167642023 , 194814973]
# """
# factor(p-1)
# 2 * 659 * 144811523 * 158091863 * 167642023 * 194814973 * 198114301509256817 * 1524198318315919100927 * 302061228405984819868188635839278249994068296319393442016901959084878254985929326557136330675603997640265462782948042782543029960066166632904951616712643712462381886167331227465971149482608610738439655060064241698902750467248492676743
# """
# 发现了,一般因子大于10 ** 9,就比较难Pohlig_Hellman出来
def r(h, g, N, p, qi):
    Zp = Zmod(p)
    h = pow(h, N//qi, p)
    g = pow(g, N//qi, p)
    ri = discrete_log(Zp(h), Zp(g)) # 只能说自动换域成了qi
    return int(ri)
m = g
n = p
c = A # B
# 发现跑到 198114301509256817 sage内核会挂掉,
# A得到crt()的结果是 826486660946153390469647371238620201,nbits() = 120
# B得到crt()的结果是 689331636356235454602319919260946857,nbits() = 120
def attack(A, g, p):
    r_list = []
    for qi in tmp_list:
        tmp = r(A,g,p-1,p,qi)
        r_list.append(tmp)
    x = crt(r_list, tmp_list)

    module = 1
    for i in tmp_list:
        module *= i
    while True:
        if int(x).bit_length()>128:
            print('fail')
            break
        if int(pow(g, x, p))==A:
            return x
        x += module

# 得到:
x = attack(A,g,p)
y = attack(B,g,p)
# x = 253100920167170926044570003097335817769
# y = 288439857354393866763185538169340562833
k = pow(g,x * y,p)
print('flag{' + hashlib.md5(str(k).encode()).hexdigest() + '}')
# flag{1515950b17d8b66efc6e47f812448721}

这个crt又让我想到了一题

题四(2020-VNCTF-CryptoSe--CRT)

题目描述:

Do you know the Chinese Remainder Theorem sometimes may not only have one solution?
import hashlib
from functools import reduce
from Crypto.Util.number import *

ms = [getRandomNBitInteger(128) for i in range(8)]
p = reduce(lambda x, y: x*y, ms)
x = getRandomRange(1, p)
cs = [x % m for m in ms]

flag = "flag{" + hashlib.sha256(str(x).encode()).hexdigest() + "}"
assert("4b93deeb" in flag)

# ms = [284461942441737992421992210219060544764, 218436209063777179204189567410606431578, 288673438109933649911276214358963643204, 239232622368515797881077917549177081575, 206264514127207567149705234795160750411, 338915547568169045185589241329271490503, 246545359356590592172327146579550739141, 219686182542160835171493232381209438048]
# cs = [273520784183505348818648859874365852523, 128223029008039086716133583343107528289, 5111091025406771271167772696866083419, 33462335595116820423587878784664448439, 145377705960376589843356778052388633917, 128158421725856807614557926615949143594, 230664008267846531848877293149791626711, 94549019966480959688919233343793910003]

题五(10DH&DLP)

题目描述:

from Crypto.Util.number import *
import hashlib


class D_H():
    def __init__(self):
        self.a = getPrime(128)
        self.b = getPrime(128)
        self.p = getPrime(1024)
        self.g = getPrime(128)
        print("p =", oct(self.p))
        print('g =', oct(self.g))

    def Alice(self):
        A = pow(self.g, self.a, self.p)
        print('A =', oct(A))

    def Bob(self):
        B = pow(self.g, self.b, self.p)
        print('B =', oct(B))

    def key_exchange(self):
        key = pow(self.g, self.a * self.b, self.p)
        return key


DH = D_H()
DH.Alice()
DH.Bob()
key = DH.key_exchange()
flag = "flag{" + hashlib.md5(str(key).encode()).hexdigest() + "}"

"""
p = 0o100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002320461720107654672567257101774542402073
g = 0o2065141432751712040220440246142105771015421
A = 0o43613040531475331260645277703363154024375552447624145423154467212453566671627031247565752140361214442354760036367561323225723114644705564775357161722423055427767542147531265612413043244400626062610066133355056455656354327315127131604056020315605217552254443755433425633455524145337447120342451441170743174123742307207301542043164563340422677
B = 0o60346662663461540211665020605034076206107430140346645020415164214412402345454104341770532135130621505726240703066624656170403450176106112505427262227076032102233051024443206700250621734407223434463067213634725130117240556410501066411312272344517322556714720077305705512606154144764640154454705523667767514601216365471777467432753500477011706
"""

题目分析:

求解出a,b,才能求出k。a, b按照提示思路求解得到的

import hashlib
p = 89884656743115795386465259539451236680898848947115328636715040578866337902750481566354238661203768010560056939935696678829394884407208311246423715319737062188883946712432742638151109800623047059726541476042502884419075341171231440736956555270413618581675255342293149119974022969989259739901207135911877936187
g = 178978976577444006672923576665226091281
A = 50239356217905865472590693130535124452580643255845661649114913837176616545680446441660877442220510431707262797664254080937029365543267241063727348540325656758225475328002443452916853398111285325871347982839528070974557886108688133202930757479488756682939702883037420270843439335589319393168138956304604603839
B = 68046726556991975149664595590016707595227397021142933235637385938062726185554085368749325804969213299585765894674681296185961941854759720420418514055458989818389672323352887448970349935846465613203461132495952672474425884078444741734977925886709761740257008768019155974481354455945984504687605522607787217862
tmp_list = [2 , 659 , 144811523 , 158091863 , 167642023 , 194814973]
# """
# factor(p-1)
# 2 * 659 * 144811523 * 158091863 * 167642023 * 194814973 * 198114301509256817 * 1524198318315919100927 * 302061228405984819868188635839278249994068296319393442016901959084878254985929326557136330675603997640265462782948042782543029960066166632904951616712643712462381886167331227465971149482608610738439655060064241698902750467248492676743
# """
def r(h, g, N, p, qi):
    Zp = Zmod(p)
    h = pow(h, N//qi, p)
    g = pow(g, N//qi, p)
    ri = discrete_log(Zp(h), Zp(g))
    return int(ri)
m = g
n = p
c = A # B
# 发现跑到 198114301509256817 sage内核会挂掉,
# A得到crt()的结果是 826486660946153390469647371238620201,nbits() = 120
# B得到crt()的结果是 689331636356235454602319919260946857,nbits() = 120
r_list = []
for qi in tmp_list:
    tmp = r(c,m,n-1,n,qi)
    print(tmp)
    r_list.append(tmp)
x = crt(r_list, tmp_list)
print(x)
module = 1
for i in tmp_list:
    module *= i
while True:
    if int(x).bit_length()>128:
        print('fail')
        break
    if int(pow(m, x, n))==c:
        print('x =', x)
        break
    x += module
# 得到:
x = 253100920167170926044570003097335817769
y = 288439857354393866763185538169340562833
k = pow(g,x * y,p)
print('flag{' + hashlib.md5(str(k).encode()).hexdigest() + '}')
# flag{1515950b17d8b66efc6e47f812448721}

局限:要知道x的位数,且一般位数相对较小,同时确定p-1光滑,但又不能太光滑(即因数太太太多,不然直接用discrete_log()就能得出了,刚刚确实试了一下,太光滑不太行(因子多,重复因子也多),到时候crt出来的位数会过于小了,太小手动扩大也不太现实,毕竟手动扩大时所加的数的位数也相对较小,但重复因子很少的话另说)只能说越小越容易crt出来
以上均为个人理解,有错误,请指出

题六(11DLP2)

题目描述:

from secret import flag
from Crypto.Util.number import *

m = bytes_to_long(flag)
assert m.bit_length() == 47
data = [2, 3, 4, 5, 6]
cs = []
p = getPrime(512)
q = getPrime(512)
n = p*q
for i in data:
    cs.append(pow(i, m, n))
print(cs)
"""
[183098212086317720236828757315510192339273033804875740822801041376722387458993552796227899411901966324318366514880536763913315608412750265815750386810801443666072579873995967676472994916617708760086271155827496223499121301339946900578686321571238854410077055150282805741312173826325669295346629270100887114, 57033027040944100515577298747833062983449034346155581535109171399667991971614122726451624246276583123688811765412755210627513537740891720491136798691017567675611749056264437247135506921747135448387155606513983563110678466501221603978172670533058510913876368172674768929417846635764813733749021238273031358064, 44177513422937659688192503808032734159830361147993789424022745838993946766432456884886189398779755202878679629523667923807343226973440318127947558457476925179848208108399587026072741947465486816791306581859876197942191253278120326733144030253277598039138046967591357892542649422324554104602547660747339872074, 12631302718057472129138289484187738038805661685759196779990368768599115569927115234126312113575785471158626604808250556471019475882601443785660157133230999298014824293609691681149195181799904358979944719852988436666306120350671288382565369084160416022015445829639878167524217607450918100132212115997395372780, 42899381454706854217031716361682990989382003331688973566655317536112884234898037025883907894505663567670829738793956574655657437562098921910867502135260835097785409348930680677890714703165271668372795217905436049326872168290466249129739381681341323649536725632009485619857375114283009023943744880775769510652]
"""

题目分析:

易知:

from Crypto.Util.number import *
from gmpy2 import gcd
out = [183098212086317720236828757315510192339273033804875740822801041376722387458993552796227899411901966324318366514880536763913315608412750265815750386810801443666072579873995967676472994916617708760086271155827496223499121301339946900578686321571238854410077055150282805741312173826325669295346629270100887114, 57033027040944100515577298747833062983449034346155581535109171399667991971614122726451624246276583123688811765412755210627513537740891720491136798691017567675611749056264437247135506921747135448387155606513983563110678466501221603978172670533058510913876368172674768929417846635764813733749021238273031358064, 44177513422937659688192503808032734159830361147993789424022745838993946766432456884886189398779755202878679629523667923807343226973440318127947558457476925179848208108399587026072741947465486816791306581859876197942191253278120326733144030253277598039138046967591357892542649422324554104602547660747339872074, 12631302718057472129138289484187738038805661685759196779990368768599115569927115234126312113575785471158626604808250556471019475882601443785660157133230999298014824293609691681149195181799904358979944719852988436666306120350671288382565369084160416022015445829639878167524217607450918100132212115997395372780, 42899381454706854217031716361682990989382003331688973566655317536112884234898037025883907894505663567670829738793956574655657437562098921910867502135260835097785409348930680677890714703165271668372795217905436049326872168290466249129739381681341323649536725632009485619857375114283009023943744880775769510652]
n = gcd(out[0]*out[1]-out[4],out[0]**2-out[2])//2
x = discrete_log_lambda(mod(out[0],n),mod(2,n),(ZZ(10**14),ZZ(10**15))) # 随便用哪对数据都行
print(long_to_bytes(x))
# flag{tql!!!}

discrete_log_lambda():
官方文档中介绍了时间复杂度:


级数可有以下对应:
2^{46} --> 10^{13}
2^{48} --> 10^{15}
时间复杂度差不多10^{7},可以爆破出来

emmm只能说 真的是 只有到了报错这一步,才会去搞懂啊,当时不太理解这个函数,用它去爆了个范围为10^{309}~10^{310}的数,我说为什么总报错,超范围了,爆到地球毁灭都爆不出

0 条评论
某人
表情
可输入 255