Pohlig-Hellman算法解决DLP问题

Pohlig-Hellman算法解决DLP问题及Python实现

I n s t r u c t i o n Instruction Instruction

Pohlig-Hellman算法适用于求解特定条件下的DLP问题(离散对数问题),以数学表达该问题即是
a x ≡ b ( m o d p ) a^x\equiv b\pmod p axb(modp)
其中 x x x即是所需要求得的数,已知 a , b , p a,b,p a,b,p该算法应用的特定条件是指 p − 1 p-1 p1是光滑数(光滑数即是该数的质因数都很小,其每个质因数大小在该算法中对应着算法复杂度)

该算法的原理及运行过程如下,需要假设其模数 p − 1 p-1 p1是光滑数

假设模 p p p的最小本原元为 g g g,那么可以用本原元来表示在模 p p p意义下的 a a a b b b
a ≡ g u ( m o d p ) b ≡ g v ( m o d p ) a\equiv g^{u} \pmod p\\ b\equiv g^{v} \pmod p agu(modp)bgv(modp)
代回到初始问题上得到
a x ≡ b ( m o d p ) ⇒ g u x ≡ g v ( m o d p ) a^x\equiv b\pmod p \\ \Rightarrow g^{ux} \equiv g^{v} \pmod p axb(modp)guxgv(modp)
从上式根据数论定理得到
u x ≡ v ( m o d p − 1 ) ux\equiv v\pmod {p-1} uxv(modp1)
这样我们可以用 u , v u,v u,v来表示出 x x x
x ≡ v ⋅ u − 1 ( m o d p − 1 ) x \equiv v\cdot u^{-1} \pmod {p-1} xvu1(modp1)
到这里我们将问题转换到了如何得到 v , u v,u v,u的大小


回到用本原元表示 a , b a,b a,b的同余式上,

  • 以解 g u ≡ a ( m o d p ) g^u \equiv a\pmod p gua(modp)为例,

我们已知 a , g , p a,g,p a,g,p;根据算法做出的假设, p − 1 p-1 p1可以被多个小质因数分解,那么
p − 1 = p 1 q 1 ⋅ p 2 q 2 ⋅ p 3 q 3 ⋯ p n q n p-1 = p_1^{q_1} \cdot p_2 ^{q_2}\cdot p_3 ^{q_3} \cdots p_n^{q_n} p1=p1q1p2q2p3q3pnqn
其中 q i q_i qi表示的是质因数的指数;我们将目标 u u u用每个素因子的大小作为进制表示,如以 p 1 p_1 p1进制表示(进制表示如果不熟悉的话可以参照二进制的表示方式)
u = c 0 + c 1 p 1 + c 2 p 1 2 + c 3 p 1 3 + ⋯ + c q 1 − 1 p 1 q 1 − 1 + ⋯ + c k p 1 k u=c_0+c_1p_1+c_2p_1^2+c_3p_1^3+\dots + c_{q_1-1}p_{1}^{q_1-1} + \cdots + c_{k} p_1^{k} u=c0+c1p1+c2p12+c3p13++cq11p1q11++ckp1k
其中 c i c_i ci均小于 p 1 p_1 p1,变量 k k k意味着可以到无限大;同理我们也将 u u u p 2 , p 3 , ⋯   , p n p_2,p_3,\cdots,p_n p2,p3,,pn进制表示,这样就得到 n n n个的等式;但是这样的进制表示并没有实质作用,因为我们很可能为了表示 u u u,而将 k k k拉到很大的数值,具体大小我们无从得知;所以我们用取模来解决这个问题,将等式两侧的大小变到我们能掌握的大小

我们在每个等式中取模 p i q i {p_i^{q_i}} piqi,将其转换为同余式,以 p 1 p_1 p1进制为例
u ≡ c 0 + c 1 p 1 + c 2 p 1 2 + c 3 p 1 3 + ⋯ + c q 1 − 1 p 1 q 1 − 1 ( m o d p 1 q 1 ) u\equiv c_0+c_1p_1+c_2p_1^2+c_3p_1^3+\dots + c_{q_1-1}p_{1}^{q_1-1}\pmod {p_1^{q_1}} uc0+c1p1+c2p12+c3p13++cq11p1q11(modp1q1)
那么将原来的 n n n个等式转换为 n n n个同余式后,联立起来得到同余式组
{ u ≡ c 0 + c 1 p 1 + c 2 p 1 2 + c 3 p 1 3 + ⋯ + c q 1 − 1 p 1 q 1 − 1 ( m o d p 1 q 1 ) u ≡ c 0 ′ + c 1 ′ p 2 + c 2 ′ p 2 2 + c 3 ′ p 2 3 + ⋯ + c q 2 − 1 ′ p 2 q 2 − 1 ( m o d p 2 q 2 ) u ≡ c 0 ′ ′ + c 1 ′ ′ p 3 + c 2 ′ ′ p 3 2 + c 3 ′ ′ p 3 3 + ⋯ + c q 3 − 1 ′ ′ p 3 q 3 − 1 ( m o d p 3 q 3 ) ⋯ u ≡ c 0 ′ ′ ′ + c 1 ′ ′ ′ p n + c 2 ′ ′ ′ p n 2 + c 3 ′ ′ ′ p n 3 + ⋯ + c q n − 1 ′ ′ ′ p n q n − 1 ( m o d p n q n ) \left\{ \begin{aligned} & u\equiv c_0+c_1p_1+c_2p_1^2+c_3p_1^3+\dots + c_{q_1-1}p_{1}^{q_1-1} \pmod {p_1^{q_1}} \\ & u\equiv c_0'+c_1'p_2+c_2'p_2^2+c_3'p_2^3+\dots + c_{q_2-1}'p_2^{q_2-1}\pmod {p_2^{q_2}} \\ & u\equiv c_0''+c_1''p_3+c_2''p_3^2+c_3''p_3^3+\dots + c_{q_3-1}''p_3^{q_3-1}\pmod {p_3^{q_3}} \\ & \cdots \\ & u\equiv c_0'''+c_1'''p_n+c_2'''p_n^2+c_3'''p_n^3+\dots + c_{q_n-1}'''p_n^{q_n-1} \pmod {p_n^{q_n}} \end{aligned} \right. uc0+c1p1+c2p12+c3p13++cq11p1q11(modp1q1)uc0+c1p2+c2p22+c3p23++cq21p2q21(modp2q2)uc0′′+c1′′p3+c2′′p32+c3′′p33++cq31′′p3q31(modp3q3)uc0′′′+c1′′′pn+c2′′′pn2+c3′′′pn3++cqn1′′′pnqn1(modpnqn)
分别对每个同余式求解 c i c_i ci得到满足单个同余式的 u u u,再对整个同余式组使用中国剩余定理得到最终满足所有同余式的解 u u u

  • 以解 u ≡ c 0 + c 1 p 1 + c 2 p 1 2 + c 3 p 1 3 + ⋯ + c q 1 − 1 p 1 q 1 − 1 ( m o d p 1 q 1 ) u\equiv c_0+c_1p_1+c_2p_1^2+c_3p_1^3+\dots + c_{q_1-1}p_{1}^{q_1-1} \pmod {p_1^{q_1}} uc0+c1p1+c2p12+c3p13++cq11p1q11(modp1q1)为例,

接下来为了将 p 1 p_1 p1进制表示的 u u u应用于求解过程,我们构造
( g u ) p − 1 p 1 r ≡ a p − 1 p 1 r ( m o d p ) (g^u)^{\frac{p-1}{p_1^r}}\equiv a^{\frac{p-1}{p_1^r}} \pmod p (gu)p1rp1ap1rp1(modp)
其中 r r r是我们设置的变量,后面会对其进行赋值;该式的模数应该也可以换作这里的 p 1 q 1 p_1^{q_1} p1q1或者是其他满足条件的数

u u u替换成 p 1 p_1 p1进制表示,得到
( g c 0 + c 1 p 1 + c 2 p 1 2 + c 3 p 1 3 + ⋯ + c q 1 − 1 p p 1 q 1 − 1 ) p − 1 p 1 r ≡ a p − 1 p 1 r ( m o d p ) (g^{c_0+c_1p_1+c_2p_1^2+c_3p_1^3+\dots + c_{q_1-1}p_{p_1}^{q_1-1}})^{\frac{p-1}{p_1^r}}\equiv a^{\frac{p-1}{p_1^r}} \pmod p \\ (gc0+c1p1+c2p12+c3p13++cq11pp1q11)p1rp1ap1rp1(modp)
我们令 r r r 1 1 1,得到
g c 0 p − 1 p 1 ⋅ g c 1 ( p − 1 ) ⋅ g c 2 ( p − 1 ) p 1 ⋯ g c q 1 − 1 ( p − 1 ) p 1 q 1 − 2 ≡ a p − 1 p 1 ( m o d p ) g^{c_0\frac{p-1}{p_1}} \cdot g^{c_1(p-1)}\cdot g^{c_2(p-1)p_1} \cdots g^{c_{q_1-1}(p-1)p_1^{q_1-2}} \equiv a^{\frac{p-1}{p_1}} \pmod p gc0p1p1gc1(p1)gc2(p1)p1gcq11(p1)p1q12ap1p1(modp)
由费马小定理 g p − 1 ≡ 1 ( m o d p ) g^{p-1}\equiv 1 \pmod p gp11(modp),上式等价于
g c 0 p − 1 p 1 ≡ a p − 1 p 1 ( m o d p ) g^{c_0\frac{p-1}{p_1}}\equiv a ^ {\frac{p-1}{p_1}} \pmod p gc0p1p1ap1p1(modp)
此时 c 0 c_0 c0的大小范围已知为 ( 0 , p 1 − 1 ) (0,p_1-1) (0,p11)之间,其余变量大小已知,我们爆破 c 0 c_0 c0大小直到满足该同余式;这里就对应了为什么该算法应用条件需要 p − 1 p-1 p1有较小的质因数

这样就可以得到 c 0 c_0 c0,但是我们要知道所有 c i c_i ci的值来表示出在模 p 1 q 1 {p_1^{q_1}} p1q1意义下的 u u u

我们通过更改 r r r的值,并应用费马小定理来达到求解 c i c_i ci的目的,如我们再令 r r r 2 2 2,得到
g c 0 p − 1 p 1 2 ⋅ g c 1 p − 1 p 1 ⋅ g c 2 ( p − 1 ) ⋯ g c q 1 − 1 ( p − 1 ) p 1 q 1 − 3 ≡ a p − 1 p 1 2 ( m o d p ) g^{c_0\frac{p-1}{p_1^2}} \cdot g^{c_1\frac{p-1}{p_1}}\cdot g^{c_2(p-1)} \cdots g^{c_{q_1-1}(p-1)p_1^{q_1-3}} \equiv a^{\frac{p-1}{p_1^2}} \pmod p gc0p12p1gc1p1p1gc2(p1)gcq11(p1)p1q13ap12p1(modp)
由费马小定理 g p − 1 ≡ 1 ( m o d p ) g^{p-1}\equiv 1 \pmod p gp11(modp),上式等价于
g c 0 p − 1 p 1 2 ⋅ g c 1 p − 1 p 1 ≡ a p − 1 p 1 2 ( m o d p ) g^{c_0\frac{p-1}{p_1^2}} \cdot g^{c_1\frac{p-1}{p_1}}\equiv a ^ {\frac{p-1}{p_1^2}} \pmod p gc0p12p1gc1p1p1ap12p1(modp)
其中同余式左侧第一项实际上我们已知,那么只用爆破 c 1 c_1 c1的大小即可;其他 c i c_i ci以此类推

当我们求出整个同余式组里的满足各个同余式模意义下的 u u u,使用孙子定理即可得到满足原始问题 g u ≡ a ( m o d p ) g^u \equiv a\pmod p gua(modp) u u u

那么用相同的方法再求解出 g v ≡ b ( m o d p ) g^v \equiv b\pmod p gvb(modp) v v v即可,代回同余式 x ≡ v ⋅ u − 1 ( m o d p − 1 ) x \equiv v\cdot u^{-1} \pmod {p-1} xvu1(modp1)求得 x x x

看完整个流程,可能会想为什么不直接对 a x ≡ b ( m o d p ) a^x\equiv b \pmod p axb(modp)使用之后将 x x x转化为 p − 1 p-1 p1的素因子的进制等等操作,这样就可以直接得到 x x x,而不用拐弯抹角地去求 u , v u,v u,v,再来求 x x x;这是因为该算法只针对原根起效,也就是说,如果 a a a是原根,那么这样操作是可以的;但如果 a a a不是原根,就无法求得正确结果,其具体原因可以见 R e f e r e n c e Reference Reference部分的文章

P y t h o n   I m p l e m e n t a t i o n Python\ Implementation Python Implementation

from Crypto.Util.number import *
from sympy.polys.galoistools import gf_crt
from sympy.polys.domains import ZZ
import gmpy2
import random

def Pohlig_Hellman(g, h, p):
    # 模数分解
    p_1 = p - 1
    d, factors = 2, []
    while d*d <= p_1:
        while (p_1 % d) == 0:
            factors.append(d)
            p_1 //= d
        d += 1
    if p_1 > 1:
        factors.append(p)
    factors = [[i, factors.count(i)] for i in set(factors)]

    # 求每个素因子进制下的c_i
    x = []
    for factor in factors:
        c_i_list = []
        for i in range(factor[1]):
            if i != 0:
                beta = (beta * pow(g, -(c_i_list[-1] * (factor[0] ** (i - 1))), p)) % p
            else:
                beta = h
            e1 = pow(beta, (p-1) // (factor[0] ** (i + 1)), p)
            e2 = pow(g, (p-1) // factor[0], p)
            for c_i in (range(factor[0])):
                if pow(e2, c_i, p) == e1:
                    c_i_list.append(c_i)
                    break
        x.append(c_i_list)

    # 将模p_i意义下的p_i进制表示转换为模p_i意义下的十进制
    system = []
    for i, factor in enumerate(factors):
        res = 0
        for j, x_j in enumerate(x[i]):
            res += x_j * (factor[0] ** j)
        res = res % (factor[0] ** factor[1])
        system.append(res)

    # 中国剩余定理
    factors = [factors[i][0] ** factors[i][1] for i in range(len(factors))]
    result = gf_crt(system, factors, ZZ)
    return result


if __name__ == "__main__":
    p = 7863166752583943287208453249445887802885958578827520225154826621191353388988908983484279021978114049838254701703424499688950361788140197906625796305008451719
    a = random.randint(0, 2 ** 512)
    x = random.randint(0, 2 ** 256)
    b = pow(a, x, p)
    print("real_x = {}".format(x))

    res = Pohlig_Hellman(a, b, p)
    print("x1 = {}".format(res))

# sage: p = 7863166752583943287208453249445887802885958578827520225154826621191353388988908983484279021978114049838254701703424499688950361788140197906625796305008451719
# sage: primitive_root(p)
# 13
    g = 13 # p的本原元为13
    u = Pohlig_Hellman(g, a, p)
    v = Pohlig_Hellman(g, b, p)
    try:
        x = gmpy2.invert(u, p - 1) * v % (p - 1)
        print("x2 = {}".format(x))
    except: # u 和 p-1 可能不互素,导致之间没有逆元;该情况将u除以最大公因数使得两者之间互素即可,最后结果除以相应的数即可
        i = 0 
        gcd = gmpy2.gcd(u, p - 1)
        while True:
            if gmpy2.gcd(u, p - 1) != 1:
                u = u // gmpy2.gcd(u, p - 1)      
                i += 1
            else:
                break
        assert gmpy2.gcd(u, p - 1) == 1
        x = (gmpy2.invert(u // gmpy2.gcd(u, p - 1), p - 1) * v) % (p - 1) // (gcd ** i)
        print("x = {}".format(x))

     
"""
a ^ x \equiv b (mod p) 之中a不是原根
real_x = 17475167858715014509948693871106677723065342839264165381680037187400995034209
x1 = 3931583376291971643604226624722943901442979289413760112577413310595676694494454509217307369704071534867821221958389972909818020158235480633350085553499260068
x = 17475167858715014509948693871106677723065342839264165381680037187400995034209
"""

"""
a ^ x \equiv b (mod p) 之中a是原根
real_x = 59538927048508916825466804038603833711975305674466217174924215808245351055236
x1 = 59538927048508916825466804038603833711975305674466217174924215808245351055236
x2 = 59538927048508916825466804038603833711975305674466217174924215808245351055236
"""


R e f e r e n c e Reference Reference

(10条消息) Pohlig-Hellman算法求解离散对数问题_国科大网安二班的博客-CSDN博客_pohlig-hellman

Pohlig_hellman/Pohlig_hellman.py at master · Sarapuce/Pohlig_hellman (github.com)

【应用代数】本原元和极小多项式 | Hope (leohope.com)

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

M3ng@L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值