离散对数问题(Discrete logarithm Problem,DLP):
已知,对于方程,求解x。
其中:p为素数,g为p的生成元
约 1634 字大约 5 分钟
2025-10-09
离散对数问题(Discrete logarithm Problem,DLP):
已知g,p,y,对于方程y≡gx(modp),求解x。
其中:p为素数,g为p的生成元
注:选择p的生成元作为g以最大程度上降低被爆破的可能性,更加安全,否则会提前进入循环
生成元:
设 G 是一个群(满足封闭性、结合律、存在单位元、每个元素有逆元的集合),若存在子集 S⊆G,使得 G 中每一个元素都可以表示为 S 中元素及其逆元的有限次乘积,则称 S 是群 G 的生成集,S 中的每个元素称为 G 的生成元。
元素的阶:
设 G 是一个群,g∈G 是群中一个元素,元素 g 的阶定义为:
满足gk=e(e 是群的单位元,gk 表示 g 自身运算 k 次,如加法群中为 kg)的最小正整数 k,记为 ord(g)
若不存在这样的正整数 k(即 gk=e 对所有 k>0 成立),则称 g 的阶为无穷大。
原根:
模 m 的原根:设 m 是正整数,a 是整数,若满足:
枚举x进行求解,通常在p很小的时候如此进行
使用的是中间相遇攻击的思想,适用于x的范围较小(修改m为int(x′),x'为x的最大范围)或者p较小的情况。
令x=im+j,其中m=n,那么整数i和j都在0∼m的范围内
有
y=gx=gim+j
y(g−m)i=gj
枚举所有的j并进行计算,将结果储存到一个集合S中,再枚举i,计算y(g−m)i,一旦发现结果在S中,则说明得到一组i和j满足需求。
import math
"""快速幂运算"""
def spow(a, b, mod):
result = 1
a = a % mod
while b > 0:
if b % 2 == 1:
result = (result * a) % mod
a = (a * a) % mod
b = b // 2
return result
"""BSGS算法"""
def BSGS(g, y, p, order=None):
if y == 1:
return 0
if order==None:
order = p - 1
m = int(math.isqrt(order)) + 1
table = {}
current = 1
for r in range(m):
if current not in table:
table[current] = r
current = (current * g) % p
gm_inv = spow(g, m * (p-2), p)
current = y % p
for q in range(m):
if current in table:
return q * m + table[current]
current = (current * gm_inv) % p
return None
g =
y =
p =
x = BSGS(g,y,p)
assert pow(g,x,p) == y
print(x)适用于生成元的阶的素因子都是大数的情形。
关键是构造ga1hb1=ga2hb2(modp),进而推导出x:
序列构造函数定义:
f(x,a,b)=⎩⎨⎧(x2modp,2amod(p−1),2bmod(p−1))xmod3=0(x∗gmodp,a+1mod(p−1),2bmod(p−1))xmod3=1(x∗hmodp,amod(p−1),b+1mod(p−1))xmod3=2
这样可以保证xk=gahb一直成立
碰撞过程:一个一次一步(龟速迭代),另外一个一次两步(兔速迭代)
当两者相等时:gahb≡gchdmodp,由于g是生成元,其幂次在模n意义下唯一,因此指数必须满足:
a+bx≡c+dxmod(p−1) (h=gx代入)
随后求解关于x的线性同余方程组
"""快速幂运算"""
def spow(a, b, mod):
result = 1
a = a % mod
while b > 0:
if b % 2 == 1:
result = (result * a) % mod
a = (a * a) % mod
b = b // 2
return result
"""扩展欧几里得算法"""
def exgcd(a, b):
if b == 0:
return (a, 1, 0)
else:
g, x, y = exgcd(b, a % b)
return (g, y, x - (a // b) * y)
"""Pollard Rho算法"""
def pollards_rho(g, h, p):
if h == 1:
return 0
if g == h:
return 1
n = p - 1
def f(x, a, b):
if x % 3 == 0:
return ((x * x) % p, (2 * a) % n, (2 * b) % n)
elif x % 3 == 1:
return ((x * g) % p, (a + 1) % n, b)
else:
return ((x * h) % p, a, (b + 1) % n)
x, a, b = 1, 0, 0
y, c, d = x, a, b
while True:
x, a, b = f(x, a, b)
y, c, d = f(y, c, d)
y, c, d = f(y, c, d)
if x == y:
da = (d - b) % n
bc = (a - c) % n
gcd_val, inv_da, _ = exgcd(da, n)
if bc % gcd_val != 0:
x, a, b = 1, 0, 0
y, c, d = x, a, b
continue
inv = (inv_da * (bc // gcd_val)) % (n // gcd_val)
for k in range(gcd_val):
candidate = (inv + k * (n // gcd_val)) % n
if spow(g, candidate, p) == h % p:
return candidate
return None
g =
y =
p =
x = pollards_rho(g,y,p)
assert pow(g,x,p) == y
print(x)适用于求解光滑阶循环群上的离散对数,即:
p−1=i=1∏npiki=p1k1∗p2k2⋯
根据拉格朗日定理,G存在阶为piki的子群,对于每个piki,构造子群元素:gi=gn/piki和hi=hn/piki,此时有gixi=himodp,xi=xmodpiki:
hi=hn/piki=(gx)n/piki=(gn/piki)x=gix
gixi=gixmodp
由于gi的阶为piki,所以指数必满足xi=xmodpiki
(子群的幂次满足模阶律:gia≡gibmodp⟺a≡bmodpiki)
因为piki较小,可以通过BSGS求出xi,最后多组(xi,piki)可以通过CRT求出x。
from sympy import factorint
from sympy.ntheory.modular import crt
import math
"""快速幂运算"""
def spow(a, b, mod):
result = 1
a = a % mod
while b > 0:
if b % 2 == 1:
result = (result * a) % mod
a = (a * a) % mod
b = b // 2
return result
"""扩展欧几里得算法"""
def exgcd(a, b):
if b == 0:
return (a, 1, 0)
else:
g, x, y = exgcd(b, a % b)
return (g, y, x - (a // b) * y)
"""BSGS算法"""
def BSGS(g, y, p, order=None):
if y == 1:
return 0
if order==None:
order = p - 1
m = int(math.isqrt(order)) + 1
table = {}
current = 1
for r in range(m):
if current not in table:
table[current] = r
current = (current * g) % p
gm_inv = spow(g, m * (p-2), p)
current = y % p
for q in range(m):
if current in table:
return q * m + table[current]
current = (current * gm_inv) % p
return None
"""Pohlig-Hellman算法"""
def pohlig_hellman(g, h, p):
if math.gcd(g, p) != 1:
raise ValueError("g必须与p互质")
n = p - 1
factors = factorint(n)
remainders = []
moduli = []
for prime, exp in factors.items():
pe = pow(prime, exp)
g_i = spow(g, n // pe, p)
h_i = spow(h, n // pe, p)
x_i = BSGS(g_i, h_i, p, pe)
if x_i is None:
return None
remainders.append(x_i)
moduli.append(pe)
x, _ = crt(moduli, remainders)
return x
g =
p =
h =
x = pohlig_hellman(g, h, p)
assert pow(g,x,p) == h
print(x)