• 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\{

    uc0+c1p1+c2p12+c3p13++cq11p1q11(modp1q1)uc0+c1p2+c2p22+c3p23++cq21p2q21(modp2q2)uc0+c1p3+c2p32+c3p33++cq31p3q31(modp3q3)uc0+c1pn+c2pn2+c3pn3++cqn1pnqn1(modpnqn)" role="presentation">uc0+c1p1+c2p12+c3p13++cq11p1q11(modp1q1)uc0+c1p2+c2p22+c3p23++cq21p2q21(modp2q2)uc0+c1p3+c2p32+c3p33++cq31p3q31(modp3q3)uc0+c1pn+c2pn2+c3pn3++cqn1pnqn1(modpnqn)
    \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
    """
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    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)

  • 相关阅读:
    Mac版本破解Typora,解决Mac安装软件的“已损坏,无法打开。 您应该将它移到废纸篓”问题
    UI设计中色彩运用应该注意哪些问题 优漫动游
    Postman内置动态参数和自定义的动态参数以及断言方式
    win10修改截图快捷键
    统信UOS操作系统 VS 银河麒麟操作系统
    phaser3 创建多个按钮
    vscode使用插件KoroFileHeader添加注释
    物理学中的不确定性的根源,有哪些看法
    Pharmacy Management System代码问题漏洞复现(CVE-2022-30887)
    【python】查询字符串a = ‘hello word‘中第二个o的索引
  • 原文地址:https://blog.csdn.net/qq_51999772/article/details/126857267