RSA中利用光滑数进行模数分解

概述

光滑数 (Smooth number):指可以分解为小素数乘积的正整数

当p是N 的因数,并且p−1是光滑数,可以考虑使用Pollard's p-1算法来分解N

当p是N的因数,并且p+1是光滑数,可以考虑使用Williams's p+1算法来分解N

Pollard的p-1算法

算法核心

为了分解合数n,设数n的一个因子是p,若p − 1的每个因子q满足 q ≤ B q\le B qB(B是自己选的一个数,称为界),此时必有

( p − 1 ) ∣ B ! (p-1) | B! (p1)B!

证:设 p − 1 = q 1 q 2 q 3 … q m p-1=q_{1}q_{2}q_{3}\dots q_{m} p1=q1q2q3qm,并且 q 1 , q 2 , q 3 , … , q m ≤ B q_{1},q_{2},q_{3},\dots ,q_{m} \le B q1,q2,q3,,qmB,显然上式成立

Pollard的p − 1算法是直接计算 a ≡ 2 B ! ( m o d n ) a \equiv 2^{B!} \pmod{n} a2B!(modn) gcd ⁡ ( a − 1 , n ) \gcd(a-1, n) gcd(a1,n),这个最大公因子就是n的一个非平凡因子

证:由于p | n,所以有

a ≡ 2 B ! ( m o d p ) a \equiv 2^{B!} \pmod{p} a2B!(modp)

又根据Fermat定理,

2 p − 1 ≡ 1 ( m o d p ) 2^{p-1} \equiv 1 \pmod{p} 2p11(modp)

因为 ( p − 1 ) ∣ B ! (p-1) | B! (p1)B!,所以

a ≡ 2 B ! ≡ 1 ( m o d p ) a \equiv 2^{B!}\equiv 1 \pmod{p} a2B!1(modp)

即 p|(a-1),于是p就是a − 1和n的公因子。最大公因子当然是其中一个

算法评价

所以只要B取值合理,可以在多项式时间计算出结果。但是B必须满足大于p − 1的所有因子,如果p − 1的因子很大,选择小的B会造成算法求解失败,选择足够大的B虽然会增加算法成功的概率,但那样的话算法的复杂度不一定比试除法好。

算法实现

def Pollard(B, n):
    a = pow(2, math.factorial(B), n)
    return math.gcd(a-1, n)

Williams的p+1算法

算法思想略(太难了,看不懂)

这两种光滑都可以用python里的第三方库primefac求解,自行pip install

pollardrho_brent(n)

Brent’s improvement on Pollard’s rho algorithm. Returns n if n is prime; otherwise, we keep chugging until we find a factor of n strictly between 1 and n.

pollard_pm1(n, B1=100, B2=1000)

Pollard’s p+1 algorithm, two-phase version. Returns n if n is prime; otherwise, we keep chugging until we find a factor of n strictly between 1 and n.

williams_pp1(n)

Williams’ p+1 algorithm. Returns n if n is prime; otherwise, we keep chugging until we find a factor of n strictly between 1 and n.

例题

import gmpy2
from libnum import n2s,s2n
from Crypto.Util.number import getPrime
import random


def gen_prime(digit):
    primes = []
    pri = 1
    while(len(primes)<100):
        pri = gmpy2.next_prime(pri)
        primes.append(int(pri))
    while True:
        count = 2
        while count < 2**digit:
            count *= random.choice(primes)
        count += 1
        if(gmpy2.is_prime(count)):
            return count

def gen_prime_2(digit):
    primes = []
    pri = 1
    while(len(primes)<100):
        pri = gmpy2.next_prime(pri)
        primes.append(int(pri))
    while True:
        count = 2
        while count < 2**digit:
            count *= random.choice(primes)
        count -= 1
        if(gmpy2.is_prime(count)):
            return count

def generate(start, modulus, a, b, c, d):
    arr = []
    arr.append(start)
    arr.append(start+1)
    arr.append(start+2)
    for i in range(10**100):
        arr.append((a * arr[-3] + b * arr[-2] + c * arr[-1] + d) % modulus)
    return arr

p1 = gen_prime(256)
q1 = getPrime(256)
p2 = gen_prime_2(256)
q2 = getPrime(256)
assert p1 > q1
assert p2 > q2
n1 = p1 * q1
n2 = p2 * q2
print "n1 = %s" % str(n1)
print "n2 = %s" % str(n2)

r = getPrime(512)
print "r = %s" % str(r)

flag = "flag{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
m = s2n(flag)

arr = generate(m, r, p1, q1, p2, q2)

print "arr[10**100] = %s" % str(arr[10**100])
print "arr[10**100+1] = %s" % str(arr[10**100+1])
print "arr[10**100+2] = %s" % str(arr[10**100+2])

# n1 = 1122627862697321019530282965736391850755580895936802291161309915792429961624747356094273651528053737694375752383507509008511083571424513544351844231796981247
# n2 = 42452228756074949430200119187674072800166259263276225653674599426683428744745000585507174701894408343398957593869403921976682474759688236534918012868451437
# r = 12562551311997602982227662257453842057851021749009292708783465660065261139703971906109320639580310598023202634257624805719964182261882169860035285540999137
# arr[10**100] = 4838353389408955215917845462224851356983185785715321739245030612236910363177888113470205298612968999913978808363082599570155255107799227941046736259627961
# arr[10**100+1] = 10699223576890057648654189340104320126576219437282628103579325446469238279292850193524138389756436672641376651989022132710127197616146958216805814995598472
# arr[10**100+2] = 10255743354332378416989087696439685123717830836779737961024112448965245238772080092701344614830923313888435306261206177140560579750917219609679498132161402

三个考点:

  • Pollard的p-1算法
  • Williams的p+1算法
  • 矩阵快速幂的运用(generate函数里的线性递推有pow(10,100)次,一个个递推是出不来的)

flag{9d3c102a-4fbc-9920-2da2-6b1e047cab37}
sagemath中的矩阵指数运算自带快速幂,附sagemath脚本:

x = #倒数第三项
y = #倒数第二项
z = #最后一项
a = 
b =    
c =    
d = 
r = 
mt=matrix(Zmod(r),4,4)
mt[0]=[c,b,a,d]
mt[1]=[1,0,0,0]
mt[2]=[0,1,0,0]
mt[3]=[0,0,0,1]

mn=matrix(Zmod(r),4,1,[z,y,x,1])
X=(mt^(pow(10,100))).solve_right(mn)
print(X)

猜你喜欢

转载自blog.csdn.net/m0_51507437/article/details/124205732
今日推荐