単純な素因数分解アルゴリズムを実装してみる

「OpenSSLとPythonでRSA暗号の原理を知る」で書いたように、RSA暗号は公開鍵である整数modulusを二つの素数素因数分解することができれば破ることができる。 ここでは、いくつかの単純な素因数分解アルゴリズムPythonで実装し、一般的なPCで解くことができるbit数などを調べてみる。 なお、正確には素因数分解ではなく、因数をひとつ見つけるアルゴリズムについて考えるものとする。

環境

Ubuntu 14.04.3 LTS 64bit版、Intel Core i5-4200U (1.6GHz * 2 * 2)

$ uname -a
Linux vm-ubuntu64 3.19.0-25-generic #26~14.04.1-Ubuntu SMP Fri Jul 24 21:16:20 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.3 LTS
Release:        14.04
Codename:       trusty

$ grep "processor\|model name" /proc/cpuinfo
processor       : 0
model name      : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
processor       : 1
model name      : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
processor       : 2
model name      : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
processor       : 3
model name      : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz

素数の生成

素数とその積の生成は、OpenSSLを用いて行うこととする。 128bitのmodulusを生成する場合は次のようになる。

$ openssl genrsa 128 | openssl rsa -noout -text
Generating RSA private key, 128 bit long modulus
.+++++++++++++++++++++++++++
.+++++++++++++++++++++++++++
e is 65537 (0x10001)
Private-Key: (128 bit)
modulus:
    00:bf:27:4e:d6:73:f6:ca:f4:f6:85:f0:b6:a0:27:
    1a:31
publicExponent: 65537 (0x10001)
privateExponent:
    76:67:8f:d3:10:e3:d7:14:d1:a8:40:83:b3:81:1b:
    ad
prime1: 16144814945699257943 (0xe00de7a77ad25e57)
prime2: 15737972014275192503 (0xda688229e3e966b7)
exponent1: 9570317772029090007 (0x84d0994ed566d8d7)
exponent2: 9046986552112602077 (0x7d8d5a560e685bdd)
coefficient: 9750135051940033389 (0x874f702bdaf8736d)

試し割り

もっとも単純なアルゴリズムであり、整数nに対してsqrt(n)以下の整数で順に割っていくというもの。 計算量はO(n1/2)である。

# trial_division.py

import sys

def isqrt(n):
    x = n
    y = (x + 1) // 2
    while y < x:
        x = y
        y = (x + n//x) // 2
    return x

def trial_division(n):
    k = isqrt(n)

    if n % 2 == 0:
        return 2
    if n % 3 == 0:
        return 3

    i = 6
    while i < k:
        if n % (i-1) == 0:
            return i-1
        if n % (i+1) == 0:
            return i+1
        i += 6

if __name__ == '__main__':
    n = long(sys.argv[1])
    p = trial_division(n)
    if p:
        print "%d = %d * %d" % (n, p, n/p)
    else:
        print "%d is prime" % n

上のコードでは、2と3の倍数となるものについては飛ばすよう若干の効率化を加えている。 また、isqrt関数は整数の範囲で平方根を求める関数である。

次の実行結果の通り、64bitの素因数分解に5分程度かかる。

$ time python trial_division.py 12814570762777948741
12814570762777948741 = 3318288047 * 3861801803

real    4m47.971s
user    4m47.296s
sys     0m0.596s

$ time python trial_division.py 18366865165381711817
18366865165381711817 is prime

real    6m15.418s
user    6m15.348s
sys     0m0.000s

Pollard-Rhoアルゴリズム + Miller-Rabin素数判定法

Pollard-Rhoアルゴリズムは、乱択アルゴリズムにより高速に因数を発見するアルゴリズムである。 整数nに対しその因数をpとしたとき、計算量はO(p1/2) <= O(n1/4)となる。

乱択アルゴリズムという性質上、与えられた数が素数だった場合、停止するまでに非常に時間がかかる。 そのため、同じく確率的アルゴリズムであるMiller-Rabin素数判定法と組み合わせ、あらかじめ素数かどうかを判定しておくことが多い。

# pollard_rho.py

import sys
from random import randint
from fractions import gcd

def miller_rabin(n, k=20):
    s, d = 0, n-1

    while d % 2 == 0:
        s += 1
        d /= 2

    for i in xrange(k):
        a = randint(2, n-1)
        x = pow(a, d, n)
        if x == 1:
            continue
        for r in xrange(s):
            if x == n-1:
                break
            x = (x*x) % n
        else:
            return False

    return True

def pollard_rho(n):
    x, y, d = 2, 2, 1

    while d == 1:
        x = (x*x + 1) % n
        y = (y*y + 1) % n
        y = (y*y + 1) % n
        d = gcd(abs(x-y), n)

    if d != n:
        return d

if __name__ == '__main__':
    n = long(sys.argv[1])
    is_prime = miller_rabin(n)
    if is_prime:
        print "%d is prime" % n
    else:
        p = pollard_rho(n)
        print "%d = %d * %d" % (n, p, n/p)

Miller-Rabin素数判定法において、素数判定を誤る確率は繰り返し回数kに対し4-kとなることが知られている。 上のコードではk = 20としている。

次の実行結果の通り、96bitの素因数分解に1分程度かかる。 なお、素数の場合についてはMiller-Rabin素数判定法により1秒未満で判定できる。

$ time python pollard_rho.py 60766145992321225002169406923
60766145992321225002169406923 = 250117558771727 * 242950340194949

real    1m12.204s
user    1m12.192s
sys     0m0.000s

$ time python pollard_rho.py 71939287897297826407363026419
71939287897297826407363026419 is prime

real    0m0.029s
user    0m0.024s
sys     0m0.004s

フェルマー

フェルマー法RSAのように素数同士の積からなる合成数において、二つの素数の差が小さい場合に有効なアルゴリズムである。 具体的には、合成数nの平方根周辺のある値をxとしたとき、(x-k)*(x+k) == nとなるkを小さいものから求める。

# fermat.py

import sys

def isqrt(n):
    x = n
    y = (x + 1) // 2
    while y < x:
        x = y
        y = (x + n//x) // 2
    return x

def is_square(n):
    if not n % 48 in (0, 1, 4, 9, 16, 25, 33, 36):
        return False

    x = isqrt(n)
    return x*x == n

def fermat(n):
    a = isqrt(n)
    b2 = a*a - n
    while not is_square(b2):
        a += 1
        b2 = a*a - n
    return a - isqrt(b2)

if __name__ == '__main__':
    n = long(sys.argv[1])
    p = fermat(n)
    if p:
        print "%d = %d * %d" % (n, p, n/p)
    else:
        print "%d is prime" % n

上のコードにおいて、is_square関数は値が平方数かどうかを判定する関数である。 平方数の判定については、平方剰余をもとに平方数となりえない値を先に判定することで効率化が図ることができ、上では法48について一度判定することで83.4%の剰余について計算を省いている。

次の実行結果の通り、64bitの素因数分解に30秒程度かかる。 素数の場合については大きな時間がかかるが、Pollard-Rhoアルゴリズムの場合と同様にMiller-Rabin素数判定法を組み合わせれば高速に判定することが可能である。

$ time python fermat.py 12814570762777948741
12814570762777948741 = 3318288047 * 3861801803

real    0m23.931s
user    0m23.924s
sys     0m0.000s

また、kが小さい場合についてはnの大きさによらず高速に素因数分解できる。 ただし、そのような素数の組が選ばれる確率は非常に小さい。

次の実行結果では、nが4096bit、kが15bitの場合について、1秒未満で素因数分解できている(参考)。

$ time python fermat.py 1034186074668005446865661651578208369514158803552244005581133348807218259696136975186768057532805396894757503164834709575083359161158060701923008831149233177215596305395749862300700772298386522636562203611668211601359078939366452933448401372783149865070405032946502692195960337152490393257718681775969357953766124434670452760182942622011519373645653657784274264774773270847089540432369489508909290697992927057404389218509541644042197448296417276686963995927414541514778710278820030649227976349765070908207584414253880096569270321293357683127769728901749940165024782079154839570419235490028108256539125090626571373643257455521820767805808214404175900736109428513289545954241551995058498737472560704736894014465695117165251699935296644992760434412206519036326801985540077086806228464411438175416317958133959586691011057808141234300883692932436022122663781160823472736298914304029803683697389880156222528045623045436226522780569221934366978973936408257173404970887883159180079205465195884955186605310665826226975454171035075016280417683011623124380312403051425940705920625660567584606796218038626946234225864706878239646554381165540673005572649955471689205027195933802181344480474070023069194083134722979170599617466579222550596280381011
1034186074668005446865661651578208369514158803552244005581133348807218259696136975186768057532805396894757503164834709575083359161158060701923008831149233177215596305395749862300700772298386522636562203611668211601359078939366452933448401372783149865070405032946502692195960337152490393257718681775969357953766124434670452760182942622011519373645653657784274264774773270847089540432369489508909290697992927057404389218509541644042197448296417276686963995927414541514778710278820030649227976349765070908207584414253880096569270321293357683127769728901749940165024782079154839570419235490028108256539125090626571373643257455521820767805808214404175900736109428513289545954241551995058498737472560704736894014465695117165251699935296644992760434412206519036326801985540077086806228464411438175416317958133959586691011057808141234300883692932436022122663781160823472736298914304029803683697389880156222528045623045436226522780569221934366978973936408257173404970887883159180079205465195884955186605310665826226975454171035075016280417683011623124380312403051425940705920625660567584606796218038626946234225864706878239646554381165540673005572649955471689205027195933802181344480474070023069194083134722979170599617466579222550596280381011 = 32158763574926282399690427421751598974822750157866002942864427634852437380540017586451493854661729909380518733649186624385206737336324813109500237603304026009112696565510846849987937423619262973393969175056759821652138869783215378169757835283584660846583208812725733839059137580944002686113912792569631796916069732431775599320458346937859589815497525828622622652165709271152246464728489927670696601016248559515951932154686633599100945314921834227324381958751184684979824241375253606863601895383658582486045363570755445629865194046700806542078378801136397577730247660070033517187439537339428288763342861366560261414507 * 32158763574926282399690427421751598974822750157866002942864427634852437380540017586451493854661729909380518733649186624385206737336324813109500237603304026009112696565510846849987937423619262973393969175056759821652138869783215378169757835283584660846583208812725733839059137580944002686113912792569631796916069732431775599320458346937859589815497525828622622652165709271152246464728489927670696601016248559515951932154686633599100945314921834227324381958751184684979824241375253606863601895383658582486045363570755445629865194046700806542078378801136397577730247660070033517187439537339428288763342861366560261446073

real    0m0.040s
user    0m0.028s
sys     0m0.012s

Common Factor Attacks

厳密には素因数分解アルゴリズムではないが、異なる二つの合成数が共通の素因数を持つ場合、合成数のbit数によらず高速に素因数を発見することができる。 具体的には、二つの合成数の最大公約数を求めるだけでよい。 最大公約数はユークリッドの互除法によりO(log n)で計算できる。

# common_factor.py

import sys
from itertools import combinations
from fractions import gcd

def common_factor(nums):
    for x, y in combinations(nums, 2):
        p = gcd(x, y)
        if p > 1:
            yield (x, y, p)

if __name__ == '__main__':
    nums = [long(line) for line in sys.stdin]
    for (x, y, p) in common_factor(nums):
        print "%d = %d * %d" % (x, p, x/p)
        print "%d = %d * %d" % (y, p, y/p)

次の実行結果の通り、共通の素因数を持つ二つの合成数において、4096bitの素因数分解が1秒未満でできている。 ただし、フェルマー法同様、そのような合成数の組が見つかる確率は非常に小さい。

$ cat nums.txt
888441243015294102172292601067855336326287332703328874021611868561802676986077663943061900147565825499428361944793850926519424097012115662952834330500310063991436323100617725235375735298880799081272752074568088718329068470304011938234831484558254069480648619902970082268415966404044075480346440255780228196130123810436581349922045986882478809572371947975635740413437554435429379257619351276688537073428555742828688693292492859848882427167863812003144627707272854531325119129193487170520323833544069156767215691882313314486711034734316444571037196897906716976294880217146223065562518732008190599420275772517007856746981048559878253715189698713288512904998165116853386358827890646734214221734467879782398263142454822004494788965625480385484067919453526130347620614241545718696612964390044265731882751353123553453763779825959232927147708421269420912056959539697543946480513329283728353598683158139025560947346471410872276377801166133810455502905521701767968469458987925204027876348478451236285710225645598004719391738052126832244276934547442298148805426263554624932225480952697394164120418952036473986289321176380982895752273796339856601757641830247264277014250785556784199680489778112198639175574756714183440252052420135756327849404813
882780483105414285207808409377995483701534823624777052872370512205663687323012030665008836246779641378802514783938670119510387540080290753721597154663983983212286475091452225736450381989144354075664524345788834071829669406861239991491714910029461119046332512912612670883516196208270591616798036275990449980681900277329633024676002455118771425041101638785071781244260968665572605189724603728273275682283698062764859089709731324988332263001477198449644990702495668478665515369529879885560385782218986658365704612364761778013317060972940381244288115042449405877429592775144911345025062002681580353283005136150916508175831243673046417028814562130687594141584703791313070099191199276692294970536889058503070974721545833923098157515201071570986923012040954935154602172279935762508063846354593464196649444856619523404920553516291837255896358713883164374689514763790547373493144894084431571218539012930850277919063121821877687816010348113929847662784908586555748228552075893306686302435532676222920629055063379540147073916377160422832146045388449909494920704515440022193230206490791891945947544516000231822642964432381556023595277757817268369138829082830952126257601345645720002969113016584602034934040121873671897644510285530925114503625051

$ time { cat nums.txt | python common_factor.py; }
888441243015294102172292601067855336326287332703328874021611868561802676986077663943061900147565825499428361944793850926519424097012115662952834330500310063991436323100617725235375735298880799081272752074568088718329068470304011938234831484558254069480648619902970082268415966404044075480346440255780228196130123810436581349922045986882478809572371947975635740413437554435429379257619351276688537073428555742828688693292492859848882427167863812003144627707272854531325119129193487170520323833544069156767215691882313314486711034734316444571037196897906716976294880217146223065562518732008190599420275772517007856746981048559878253715189698713288512904998165116853386358827890646734214221734467879782398263142454822004494788965625480385484067919453526130347620614241545718696612964390044265731882751353123553453763779825959232927147708421269420912056959539697543946480513329283728353598683158139025560947346471410872276377801166133810455502905521701767968469458987925204027876348478451236285710225645598004719391738052126832244276934547442298148805426263554624932225480952697394164120418952036473986289321176380982895752273796339856601757641830247264277014250785556784199680489778112198639175574756714183440252052420135756327849404813 = 30094504000188239020151081364836978642515567675718773053464099309910028925560991426570939310582088936607583861708569872351068260656205876930160648487885561172643428908022945411031069520952948240317537081178371267446884744948251284183621948670880897605924632767128833848853213483976135804220279591102249759334548097546952384012455150437958938593286685443258091084425290523032227270109892468085164321266154276823025471164370162127401053735334983910367679071627280601662913673154344586359423856921429216035850978379728954418530655343614680839905243246402183753209499080677780527109087925474741203711791946310699728133647 * 29521710775154724738536772527584123117558710613185797920601145487104166977711662880879179271048876405095288045905017016692076383141051745304194231171302588112692719379484816312610044960068902592215853578316022870490297200043945853963854204074128066849603964094184178301412655771436359730063212919768104154891433003679579996888043953631799144984305859356177785182529634481307702685821400386110992830487859752626831497462372631255954636435957631272568434117771136620170717336511538556050260061771074028879608580486351337906026632036083341002564850216617332234416072504420109009193206508593690307919068066529130502806179
882780483105414285207808409377995483701534823624777052872370512205663687323012030665008836246779641378802514783938670119510387540080290753721597154663983983212286475091452225736450381989144354075664524345788834071829669406861239991491714910029461119046332512912612670883516196208270591616798036275990449980681900277329633024676002455118771425041101638785071781244260968665572605189724603728273275682283698062764859089709731324988332263001477198449644990702495668478665515369529879885560385782218986658365704612364761778013317060972940381244288115042449405877429592775144911345025062002681580353283005136150916508175831243673046417028814562130687594141584703791313070099191199276692294970536889058503070974721545833923098157515201071570986923012040954935154602172279935762508063846354593464196649444856619523404920553516291837255896358713883164374689514763790547373493144894084431571218539012930850277919063121821877687816010348113929847662784908586555748228552075893306686302435532676222920629055063379540147073916377160422832146045388449909494920704515440022193230206490791891945947544516000231822642964432381556023595277757817268369138829082830952126257601345645720002969113016584602034934040121873671897644510285530925114503625051 = 30094504000188239020151081364836978642515567675718773053464099309910028925560991426570939310582088936607583861708569872351068260656205876930160648487885561172643428908022945411031069520952948240317537081178371267446884744948251284183621948670880897605924632767128833848853213483976135804220279591102249759334548097546952384012455150437958938593286685443258091084425290523032227270109892468085164321266154276823025471164370162127401053735334983910367679071627280601662913673154344586359423856921429216035850978379728954418530655343614680839905243246402183753209499080677780527109087925474741203711791946310699728133647 * 29333611316534492642114695914361875827353936602039611693387782601066021747108940770284205684743192567435597448844270242635706940719055360846091860177842785258548515263955446733702873336801213730696067625102999759204582858629776679167503390124589638053007027785917641140606694678963338089301468801125801144536374695406033850449781597771312803137398214729077301657987716032422612267398441299477065463230629907039871180083779210842889144946322439175733816114014531390999811837034265672904377154925113301930277530009488651707667660960383253017461910446471490714077271746064722611111519237800570978700971166429844348550133

real    0m0.029s
user    0m0.028s
sys     0m0.000s

関連リンク