在一个群 G 中,如果 g 是 G 的生成元,即所有 G 中的所有元素都可以被表示成 $y=g^x$,此时的 x 称为 y 在 G 中的对数。
阶
设 m≥1,gcd(a,m)=1,使得 $a^d≡1(\mod m)$ 成立的最小正整数 d 称为 a 对模 m 的阶。一般将其计为 δm(a)。
满足 $a^d≡1(\mod m) $的 d 一定满足 d∣φ(m)
原根¶
当 δm(a)=φ(m) 时,称 a 是模 m 的原根,简称 m 的原根。
只有$ m=2,4,p^α,2p^α$(p 为奇素数,α 为正整数)时,模 m 的剩余系存在原根。(充要条件)
以题代入
2020网鼎杯-青龙组-you raise me up
1 2 3 4 5 6 7 8 9 10 11
from Crypto.Util.number import * import random
n = 2 ** 512 m = random.randint(2, n-1) c = pow(m, bytes_to_long(flag), n) print'm = ' + str(m) print'c = ' + str(c)
# m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075 # c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499
可知,flag被当作e了,即:$c = m^{flag} \mod n$
那朴素的想,这里最直接的方法就是求出在mod n的前提下,求出$\log{m}^{c}$
看到n = 2**512,则:phi= 2**511
即:phi易求
离散对数分解问题(DLP)
sage中的DLP相关函数
参数说明:
求解以base为底
a的对数
ord为base的阶,可以忽略
operation可以是+与*,默认为*
bounds是一个区间(ld,ud),需要保证所计算的对数在此区间内
函数使用
discrete_log(a,base,ord,operation)
通用的求离散对数的方法(Pohlig-Hellman+?BSGS)
(但没有bounds!)
Pohig-Hellman算法是一种求解光滑阶循环群上的离散对数的方法。
要求$a≡g^x(\mod p)$中 g必须是原根 (对于 g 不是原根的情况,需要利用原根将原方程转化成两个关于原根的离散对数问题。
#Sage def brute_dlp(gi, ci, n, lim): bi = gi for i in range(1, lim+1): if bi == ci: return i bi = (bi * gi) % n print("[-] NOT in the range") print("[-] Something's Wrong, you gotta check the range", lim)
def pohlig_hellman(g, c, s, n, factors): res = [] modulus = [] for q, e in factors: assert pow(g, s//(q**e), n) != 1 gi = pow(g, s//(q**e), n) ci = pow(c, s//(q**e), n) dlogi = brute_dlp(gi, ci, n, q**e) print("[+] dlog modulo {0} == {1}".format(q**e, dlogi)) res.append(dlogi) modulus.append(q**e) print("\n[*] res = ", res) print("[*] modulus = ", modulus) dlog = CRT(res, modulus) print("\n[+] dlog modulo {0} == {1}".format(prod(modulus), dlog)) return dlog
p = P = PolynomialRing(Zmod(p), name='x') x = P.gen() n = g = nfactors = n.factor() s = 1 for i in nfactors: s *= p**(i[0].degree()) - 1
print(s) print(factor(s)) qe = dlog = pohlig_hellman(g, c, s, n, qe)
flag = bytes.fromhex(hex(dlog)[2:]) print("\n[*] flag = ", flag.decode())
#生成64位的素数p作为模数,int为32位,超过int要在数字后加L p=random_prime(2L**64) #定义有限域GF(p) G=GF(p) #找一个模p的原根 gp ('znprimroot('+str(p)+')') #输出Mod(rt,p),则x是模p的原根 g=G(rt) #生成私钥 x=G(ZZ.random_element(p-1)) #公钥y=g^x mod p,由于已经定义在GF(p)上,因此g**x就是g^x mod p y=g**x #计算离散对数的通用方法 discrete_log(y,g)==x #n为合数(Pohlig-Hellman) x = discrete_log(mod(b,n),mod(a,n)) #n为质数或质数幂(线性筛Index Calculus) R = Integers(99) a = R(4) b = a^9 b.log(a) #或 x = int(pari(f"znlog({int(b)},Mod({int(a)},{int(n)}))")) x = gp.znlog(b, gp.Mod(a, n)) #计算离散对数的lambda方法 discrete_log_lambda(y,g,(floor(ZZ(x)/2),2*ZZ(x)))==x #小步大步法计算离散对数 bsgs(g,y,(floor(ZZ(x)/2),2*ZZ(x)))==x
回到题目,易得:
1 2 3 4 5 6 7 8
n = 2 ** 512 m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075 c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499
m = Mod(m, n) c = Mod(c, n)
discrete_log(c, m)
此外,针对Pohlig-Hellman,给出以下例题
1 2 3 4 5 6 7 8 9 10 11 12 13
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
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 defr(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)) returnint(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 whileTrue: ifint(x).bit_length()>128: print('fail') break ifint(pow(m, x, n))==c: print('x =', x) flag = "flag{"+hashlib.md5(str(x).encode()).hexdigest()+"}" print(flag) break x += module # x = 256148714020855512578941590011688772421 # flag{bbb04cf5180893e23e559c63ceae5b8f}
from Crypto.Util.number import * from flag import flag
defgen_primes(nbit, imbalance): p = 2 FACTORS = [p] while p.bit_length() < nbit - 2 * imbalance: factor = getPrime(imbalance) FACTORS.append(factor) p *= factor rbit = (nbit - p.bit_length()) // 2
whileTrue: r, s = [getPrime(rbit) for _ in'01'] _p = p * r * s if _p.bit_length() < nbit: rbit += 1 if _p.bit_length() > nbit: rbit -= 1 if isPrime(_p + 1): FACTORS.extend((r, s)) p = _p + 1 break
FACTORS.sort() return (p, FACTORS)
defgenkey(nbit, imbalance, e): whileTrue: p, FACTORS = gen_primes(nbit // 2, imbalance) iflen(FACTORS) != len(set(FACTORS)): continue q, q_factors = gen_primes(nbit // 2, imbalance + 1) iflen(q_factors) != len(set(q_factors)): continue factors = FACTORS + q_factors if e notin factors: break n = p * q return n, (p, q)
# Pollard‘s p-1 method # https://blog.csdn.net/m0_62506844/article/details/125774485 from gmpy2 import * N = 7069789930583271525053215046247773438899869283661158227309691853515987055334306019600324056376312479212090202373516405860759222837585952590589336295698718699890424169542280710721069784487366121478569760563045886361884895363592898476736269784284754788133722060718026577238640218755539268465317292713320841554802703379684173485217045274942603346947299152498798736808975912326592689302969859834957202716983626393365387411319175917999258829839695189774082810459527737342402920881184864625678296442001837072332161966439361793009893108796934406114288057583563496587655548536011677451960307597573257032154009427010069578913 a = 2 n = 2 while True: a = powmod(a, n, N) res = gcd(a-1, N) ifres != 1andres != N: q = N // res print("p=",res) print("q=",q) break n += 1
from Crypto.Util.number import * N = 7069789930583271525053215046247773438899869283661158227309691853515987055334306019600324056376312479212090202373516405860759222837585952590589336295698718699890424169542280710721069784487366121478569760563045886361884895363592898476736269784284754788133722060718026577238640218755539268465317292713320841554802703379684173485217045274942603346947299152498798736808975912326592689302969859834957202716983626393365387411319175917999258829839695189774082810459527737342402920881184864625678296442001837072332161966439361793009893108796934406114288057583563496587655548536011677451960307597573257032154009427010069578913 c_1 = 488692928085934899944055554857568564903346089951134051486941368561567330884363274156339625953702601270565654444836193796061118053575538224794730472032345171432952984560662218697488844007827176184413713651118743456250147472678673801728916283759932987216388378211555067885210167894310696549664382751443669387953644382833924884208966436685137553434532738845959014828804809425096115758364136546390809453200055265653531950423111482644330073443545410319576097902472017235065047191133112557289289189187696092145850680765843608118584107829268136014912479701945735063525799796920293418182776436767911172221104640501952880057 c_2 = 109770827223661560471527567179288748906402603483328748683689436879660543465776899146036833470531024202351087008847594392666852763100570391337823820240726499421306887565697452868723849092658743267256316770223643723095601213088336064635680075206929620159782416078143076506249031972043819429093074684182845530529249907297736582589125917235222921623698038868900282049587768700860009877737045693722732170123306528145661683416808514556360429554775212088169626620488741903267154641722293484797745665402402381445609873333905772582972140944493849645600529147490903067975300304532955461710562911203871840101407995813072692212 e = 65537
p = 83408372012221120677052349409462320990177094246143674474872152829440524098582262384066400107950985845255268335597502228206679771838750219696329523257176739436871327238322817403970284015587320158034304282786944710043150568360761457471641695390427267786485448748458445872307883254297662715749746270343116946519
from Crypto.Util.number import getPrime, bytes_to_long as b2n, long_to_bytes as n2b from gmpy2 import invert from random import randint
deffind_t(a, m, n): _y = 1 while m > 0: r = m % 2 if r == 1: _y = (_y * a) % n a = a * a % n m = m // 2 return _y
defprimitive_root(pi):# 找对于模p的有效生成元g for a inrange(2, pi): flag = 1 for i inrange(1, pi): if find_t(a, i, pi) == 1and i < pi - 1: flag = 0 elif flag and find_t(a, i, pi) == 1and i == pi - 1: return a