序文 §

模範的なCryptoプレイヤーなので、突如Coppersmith's Attackを実装していない事に恐れを抱いてしまった。というわけで再実装をする。

本音を言えば、元の原理を知っていれば多変数や法の約数を法とした場合などへの応用がしやすくなるんじゃないかと思ったので取り組み始めた次第である。

更に本音を重ねると、その最中に見つけたわかりやすい資料の知名度が他のCryptoやCTF関連資料に比べて妙に低いと思ったのでそれを紹介することも兼ねている、というかこちらが最大の目的である。車輪の再発明をしただけのこんな記事を読んでないで一番下までスクロールして参考資料をクリックしてダウンロードしてタブを閉じましょう。


冗談はさておき、今回はZ/nZ\mathbb Z/n\mathbb Z上の単変数モニック多項式の小さい根を求めるアルゴリズムをSageMathで実装する。

Prerequisites §

  • 格子基底簡約
    • LLLで十分だが、Coppersmith's Attackで求められる解の限界を評価するためにはLLLで求められる最も小さい基底のノルムの上界を知っておく必要がある

Outline §

アルゴリズムと実装の解説に入る前にアルゴリズムの概観を説明する。

まず前提となるのは、ある多項式fff(x0)0mod  nf(x_0) \equiv 0 \mod nとなる時、ffの定数倍: af(x)af(x)や他の多項式を掛けたもの: f(x)h(x)f(x)h(x)、同じ根x0x_0を持つ多項式g(x)g(x) (すなわち、g(x0)0mod  ng(x_0) \equiv 0 \mod n)との和: f(x)+g(x)f(x) + g(x)もまたx0x_0を根として持つという事実である。

これによって、ffを起点として同じ根を持つ多項式を複数用意することが出来る。そして、それらの整数係数の線形結合もx0x_0を根として持つことから、線形結合によって作られた多項式の中で係数の絶対値が小さくなるようなものを用意する。ここで基底簡約が使われる。

これで得られた多項式は係数が小さいことから、それに値を代入した結果も小さくなることが期待出来る。そして、もしその多項式にx0x_0を代入した結果の絶対値がnnより小さければ、その多項式をf0f_0とおくとf0(x0)=0f_0(x_0) = 0となるはずである (法を取らずとも0である)。

法が無い場合の方程式は様々な方法によって簡単に解けるので、それで解けばx0x_0が得られる。

以上がCoppersmith's Attackの大まかな説明である。

Howgrave-Graham's Lemma §

前節では最終的に法がなくとも同じ根を持つような多項式を構成することを言ったが、具体的にそういう方程式はどのような条件で実現できるのかを考える。これに対する答えがHowgrave-Graham's Lemmaであり次である。

nnを法としたee次多項式f(x)f(x)に対して、ある数XXが存在し、ffの根x0x_0についてx0<X|x_0| \lt Xであると仮定する (つまり、XXx0|x_0|の上界である)。

この時、次が成立するならf(x0)=0f(x_0) = 0である。

f(xX)<ne+1 |f(xX)| \lt \frac n{\sqrt {e+1}}

ここで、多項式f(x)=i=0eaixif(x) = \sum_{i=0}^e a_ix^iに対するノルムf(x)|f(x)|を次で定義する。

f(x)=i=0eai2 |f(x)| = \sqrt{\sum_{i=0}^e a_i^2}

証明は次のようになる。

まず次の不等式が成り立つ。

f(x0)i=0eaix0ii=0eaiXi |f(x_0)| \leq \sum_{i=0}^e |a_ix_0^i| \leq \sum_{i=0}^e |a_i|X^i

ここで最左辺はベクトル(a0,a1X,,aeXe)(|a_0|, |a_1|X, \dots, |a_e|X^e)と1がe+1e+1個並んだベクトル(1,1,,1)(1,1,\dots,1)の内積で表される。

ベクトルの内積はノルムの積以下であるという事実を用いて次が成り立つ。

i=0eaiXi=(a0,a1X,,aeXe),(1,1,,1)(a0,a1X,,aeXe)(1,1,,1)=f(xX)e+1 \begin{aligned} \sum_{i=0}^e|a_i|X^i &= \langle (|a_0|, |a_1|X, \dots, |a_e|X^e), (1,1,\dots,1)\rangle\cr &\leq |(|a_0|, |a_1|X, \dots, |a_e|X^e)||(1,1,\dots,1)|\cr &=|f(xX)|\sqrt{e+1} \end{aligned}

ここでf(xX)<ne+1|f(xX)| \lt \frac n{\sqrt {e+1}}を仮定しているので、最終的に次の不等式が得られる。

f(x0)i=0eaiXif(xX)e+1<ne+1e+1=n \begin{aligned} |f(x_0)| &\leq \sum_{i=0}^e|a_i|X^i \cr &\leq |f(xX)|\sqrt{e+1}\cr &\lt \frac{n}{\sqrt{e+1}}\sqrt{e+1} \cr &= n \end{aligned}

この不等式より、n<f(x0)<n-n \lt f(x_0) \lt nが得られるが、f(x0)0mod  nf(x_0) \equiv 0 \mod nであったから、f(x0)=nkf(x_0) = nkとなる何らかの整数kkが存在する。このようなkkは不等式よりk=0k=0しかあり得ず、したがってf(x0)=0f(x_0) = 0となる。\Box

他の多項式を集める §

Howgrave-Graham's Lemma(以下、「補題」とする)より目的とする多項式の条件が判明した。続いての目標はこのような多項式の構成であり、そのために同じ根を持つ多項式の線形結合を用いる。

無闇矢鱈と多項式の線形結合(スカラー倍や多項式同士の和)を繰り返していても意味が無く、後述するようにこれはLLLのような格子の基底簡約アルゴリズムが上手くやってくれる。そこでここでは線形結合に使えそうな別の多項式を集めることを考える。

f(x)f(x)に対して、f(x)2f(x)^2もまたx0x_0を根として持つが、ノルムが大きくなってしまうのであまり意味が無い。結局候補となるのは次のような式である。

f0(x)nf1(x)nxfe1(x)nxe1fe(x)f(x) \begin{aligned} f_0(x) &\coloneqq n\cr f_1(x) &\coloneqq nx\cr &\vdots\cr f_{e-1}(x) &\coloneqq nx^{e-1}\cr f_e(x) & \coloneqq f(x) \end{aligned}

これらは全てx0x_0を根として持つことから、基底として使えそうである。

また、これより更に良い結果を得る方法が存在する。補題の右辺を見直して見るとne+1\frac n{\sqrt{e+1}}が多項式のノルムの上界となっており、nnに対してO(n)O(n)の大きさであるが、n2n^2を法として根がx0x_0である多項式を用意することで、これをO(n2)O(n^2)にすることが出来る。

もちろん、そのような多項式は、先程棄却したf(x)2f(x)^2のようなものとなり、ノルムも大きくなる。補題の左辺のxxの係数が単純計算でnnと同じく2乗程度の増加を見せ、e2e^2の次数においてはその係数が(Xe)2(X^e)^2となり、多項式のノルムもこのぐらいになるが、x0|x_0|nnに対して有意に小さいことから、XXも小さくとることが出来る。よって、右辺の2乗による増加は左辺の2乗による増加に比べてかなり大きくなることから、より有利な条件となる。

では、実際にn2n^2を法としてx0x_0を根に持つ多項式を用意してみる。次のような多項式fi,j (0i2)f_{i,j} \ (0\leq i \leq 2)n2n^2を法として根x0x_0を持つ。

fi,j(x)=n2ixjf(x)i f_{i,j}(x) = n^{2-i}x^jf(x)^i

f(x0)=nkf(x_0) = nkとなる整数kkが存在するから、f(x)i=nikif(x)^i =n^ik^iとなり、n2if(x0)i=n2ki0mod  n2n^{2-i}f(x_0)^i =n^2k^i \equiv 0 \mod n^2となるから、fi,j(x0)0mod  n2f_{i,j}(x_0) \equiv 0 \mod n^2である。

同様にしてn3,n4,n^3, n^4,\dotsのように法を大きくしていくと、それを法としてx0x_0が根となる複数の多項式が得られる。

格子基底簡約 §

前節で多項式が得られたので、これがHowgrave-Graham's Lemmaを適用出来るぐらい小さくする。これには(この節のタイトル通り)格子基底簡約を用いる。目標は次のようなベクトルを基底として持つような格子行列の導出である。

c=(c0,c1X,,ceXe) \boldsymbol c = (c_0, c_1X, \dots, c_eX^e)

当然だが、各cic_iに対してg(x)i=0ecixig(x) \coloneqq \sum_{i=0}^e c_ix^iとしてg(x0)0mod  ng(x_0) \equiv 0 \mod nとなるような簡約を行う。

この時、c\boldsymbol cのノルムは、g(xX)|g(xX)|に等しいことが定義からわかる。基底を簡約していることから、c\boldsymbol cのノルムは小さくなっていることが期待され、補題の条件を満たす程度になっていればx0x_0を求めることが出来る。

では肝心の簡約する基底はどうするかというと次のようにする。

まずf(x)f(x)に対して、ある整数m1m \geq 1を用意し、前節のように、fi,j(x)nmixjf(x)if_{i,j}(x) \coloneqq n^{m-i}x^jf(x)^i0im,0je10 \leq i \leq m, 0 \leq j \leq e-1の範囲で用意する。この時、degfi,j=j+ei\deg {f_{i,j}} = j + eiとなることから、i,ji,jが異なればfi,jf_{i,j}の次数も異なる。また、degfi,j\deg f_{i,j}の最大値はem+e1em+e-1となる。0kem+e10 \leq k \leq em+e-1となる整数kkに対して、degfi,j=k\deg f_{i,j} = kとなるfi,jf_{i,j}が一意的に存在することから、多項式はem+e1+1=e(m+1)em+e-1+1 = e(m+1)本得られることになる。以後、de(m+1)d\coloneqq e(m+1)とおく。

ここで多項式fi,j(xX)f_{i,j}(xX)を計算し、その係数を成分にとる次のような行列MMを作る。但し、fi,j(k)(x)f^{(k)}_{i,j}(x)で、多項式fi,j(x)f_{i,j}(x)kk次の係数を指すものとする。また、括弧内はxXxXであり、XXは変数ではないことから、XXのべきが係数に掛けられる事にも注意する。

Mi,j=fk,l(j)(xX)   (degfk,l(xX)=l+ek=i) M_{i,j} = f_{k,l}^{(j)}(xX) \ \ \ (\deg f_{k,l}(xX) = l+ek = i)

すなわち、ii行目の成分は次数がiiであるfk,l(xX)f_{k,l}(xX)の係数が順に並ぶ。e=3,m=3e=3, m=3で例を構成してみる。この場合はd=e(m+1)=12d = e(m+1)=12となるから12本の多項式が得られる。

fi,j(x)f_{i,j}(x)は次のようになる。

i,ji,jdegfi,j\deg{f_{i,j}}fi,jf_{i,j}
0,00n3n^3
0,11n3xn^3x
0,22n3x2n^3x^2
1,03n2f(x)n^2f(x)
1,14n2xf(x)n^2xf(x)
1,25n2x2f(x)n^2x^2f(x)
2,06nf(x)2nf(x)^2
2,17nxf(x)2nxf(x)^2
2,28nx2f(x)2nx^2f(x)^2
3,09f(x)3f(x)^3
3,110xf(x)3xf(x)^3
3,211x2f(x)3x^2f(x)^3

よって、行列MMは対角成分にのみ注目すると、次のようになる。f(x)f(x)がモニック多項式であることから、fi,j(x)f_{i,j}(x)もモニック多項式であり、fi,j(xX)f_{i,j}(xX)の最高次数にXXのべきが現れることに注意する。

(n3n3Xn3X2n2X3n2X4n2X5nX6nX7nX8X9X10X11) \begin{pmatrix} n^3 \cr & n^3X \cr & & n^3X^2 \cr & & & n^2X^3 \cr & & & & n^2X^4 \cr & & & & & n^2X^5 \cr & & & & & & nX^6 \cr & & & & & & & nX^7 \cr & & & & & & & & nX^8 \cr & & & & & & & & & X^9 \cr & & & & & & & & & & X^{10} \cr & & & & & & & & & & & X^{11} \cr \end{pmatrix}

この行列の左下には各fi,j(xX)f_{i,j}(xX)の他の次数における係数が並んでいる。また、jj列目の成分はfk,l(xX)f_{k,l}(xX)jj次係数であるから、XjX^jfk,l(x)f_{k,l}(x)jj次係数の積となり、CXjCX^jとなんらかの整数CCを用いて表される。

一方、右上は全て0である下三角行列である。よって、行列式は対角成分の積となる (この後の基底簡約の評価で用いる)。

この基底行列に左から係数ベクトル(a0,a1,,ad1)Zd(a_0, a_1,\dots, a_{d-1}) \in \mathbb Z^dを掛けると(b0,b1X,,bd1Xd1)(b_0, b_1X, \dots, b_{d-1}X^{d-1})が現れる。これらの関係は次のようになっている。

biXi=j=0d1ajfk,l(i)(xX)   (degfk,l=l+ke=j) b_iX^i = \sum_{j=0}^{d-1} a_jf^{(i)}_{k,l}(xX) \ \ \ (\deg f_{k,l} = l+ke = j)

したがって、biXb_iXfk,l(xX)f_{k,l}(xX)の整数係数の線形結合で出来た多項式からii次の係数を取り出したものになる。

以上より多項式h(xX)=i=0d1biXixih(xX) = \sum_{i=0}^{d-1}b_iX^ix^iとおくと、h(x)=i=0d1bixih(x) = \sum_{i=0}^{d-1}b_ix^iであり、更にh(xX)=(b0,b1X,,bd1Xd1)|h(xX)| = |(b_0, b_1X, \dots, b_{d-1}X^{d-1})|である。

また、次が成り立つ。

h(xX)=i=0d1aifk,l(xX)   (degfk,l=l+ke=i) h(xX) = \sum_{i=0}^{d-1}a_if_{k,l}(xX) \ \ \ (\deg f_{k,l} = l+ke = i)

ここでxX=x0xX = x_0を代入すると、h(x0)0mod  nmh(x_0) \equiv 0 \mod n^mとなる。よってMMが張る格子に含まれるベクトルから、hhのように多項式を構成すればx0x_0が根となる多項式を得ることが出来る。

目的としていたのは、ノルムが小さい多項式であったから、hhii次係数bib_iを小さくすると考えるのが自然である。そこで格子基底簡約が登場する。

基底簡約のアルゴリズムには色々あるが、今回は昨今のCTFプレイヤーに人気なLLLを用いる (ついでに解の上界であるXXの評価も楽になる)。MMをLLL簡約して出てきたベクトルをb(b0,b1X,,bd1Xd)\boldsymbol b \coloneqq (b_0, b_1X, \dots, b_{d-1}X^d)とおく。

既に示しているようにb\boldsymbol bから多項式hhを構成すると、h(xX)=b|h(xX)| = |\boldsymbol b|であるから、Howgrave-Graham's Lemmaの仮定を満たすには次が成り立つ必要がある。

bnmd |\boldsymbol b| \leq \frac{n^m}{\sqrt d}

右辺がこのようになるのは、法をnnからnmn^mにしており、得ている多項式の最大次数がem+e1em + e - 1であることに注意する。

よって、MMを簡約して出てきたベクトルがこれを満たしているなら、根であるr0r_0を求めることが出来る。次の節でこの不等式を満たすようなXX、つまり解の上界が何であるかを評価する。

XXの条件 §

先に述べたように、MMは下三角行列であるから行列式はその対角成分の積になる。この構成からM|M|を計算すると次のようになる。

M=n(i=0mie)X(i=0d1i)=nm(m+1)2eXd(d1)2=nmd2Xd(d1)2 |M| = n^{\left(\sum_{i=0}^m i\cdot e\right)}X^{\left(\sum_{i=0}^{d-1}i\right)} = n^{\frac{m(m+1)}2e} X^{\frac{d(d-1)}2} = n^{\frac {md}2} X^{\frac{d(d-1)}2}

計算を簡単にするため、LLLの簡約パラメータδ\deltaδ=34\delta = \frac 34とするとMMをLLL簡約した時の最も短いベクトルの大きさは2d14M1d2^{\frac{d-1}4}|M|^\frac 1d以下になるため、次が成り立つ。

b2d14M1d=2d14nm2Xd12 |\boldsymbol b| \leq 2^{\frac{d-1}4}|M|^{\frac 1d} = 2^{\frac{d-1}4} n^{\frac m2} X^{\frac {d-1}2}

この式から、b|\boldsymbol b|の上界はわかっているので、それがこの式の右辺より小さいような場合を考える。したがって次のような不等式を満たすようなXXを考えることになる。

2d14nm2Xd12nmd 2^{\frac{d-1}4} n^{\frac m2} X^{\frac {d-1}2} \leq \frac{n^m}{\sqrt d}

これを満たすようなXXであればHowgrave-Graham's Lemmaからx0x_0を求めることが出来る。

XXだけの不等式にするために、両辺2d1\frac{2}{d-1}乗して移項すると次のようになる。

Xnmd1d1d1212=12nmd1d1d1 X \leq \frac{n^{\frac{m}{d-1}}}{d^{\frac 1{d-1}}}\cdot 2^{-\frac 12} = \frac 1{\sqrt 2}\cdot \frac{n^{\frac{m}{d-1}}}{d^{\frac 1{d-1}}}

ここで、md1\frac{m}{d-1}に関して次の不等式が成り立つ。

md1>md=me(m+1)=1e1e(m+1)>1e1m \frac{m}{d-1} \gt \frac{m}d = \frac{m}{e(m+1)} = \frac{1}{e} - \frac{1}{e(m+1)} \gt \frac 1e - \frac 1m

よって、nnの指数として次が成り立つ。

n1e1mnmd1 n^{\frac 1e - \frac 1m} \leq n^{\frac{m}{d-1}}

また、d1d1d^{\frac 1{d-1}}に関して次の不等式が成り立つ。

d1d1d1d1d1d1 d^{\frac 1{d-1}} \leq d \Leftrightarrow \frac 1d \leq \frac 1{d^{\frac 1{d-1}}}

よって次の不等式が成り立つ。

12n1e1md12nmd1d1d1 \frac 1{\sqrt 2}\frac{n^{\frac 1e - \frac 1m}}{d} \leq \frac 1{\sqrt 2}\frac{n^{\frac{m}{d-1}}}{d^{\frac 1{d-1}}}

したがって、もし左辺がXXより大きかったら、右辺も大きくなる。式にすると次のようになる。

X12n1e1mdX12nmd1d1d1 X \leq \frac 1{\sqrt 2}\frac{n^{\frac 1e - \frac 1m}}{d} \Rightarrow X \leq \frac 1{\sqrt 2} \frac{n^{\frac{m}{d-1}}}{d^{\frac 1{d-1}}}

右側の不等式を満たしているならHowgrave-Graham's Lemmaの仮定も満たしていることになるので、x0x_0を求めることが出来るようなXXの条件はX12n1e1mdX \leq \frac 1{\sqrt 2}\frac{n^{\frac 1e - \frac 1m}}{d}である。

実装 §

mmXXは指定する必要がある。

def solve(f, m, X):
    e = f.degree()
    d = e*(m+1)
    n = f.base_ring().order()
    f = f.change_ring(ZZ)
    var = f.variables()[0]
    coeffs = f.coefficients()

    fs = []
    for _d in range(d):
        i,j = _d // e, _d % e
        _f = n^(m-i) * var^j * f^i
        fs.append(_f(var*X))

    M = []
    for _f in fs:
        _coeffs = _f.coefficients(sparse=False)
        _coeffs += [0 for _ in range(d - len(_coeffs))]
        M.append(_coeffs)

    M = matrix(ZZ, M)

    PRZ.<r> = PolynomialRing(ZZ)
    B = M.LLL()

    ret = set()
    for b in B:
        b = [_b // X^i for i, _b in enumerate(b)]
        h = PRZ(b)
        rs = h.roots()
        if len(rs) > 0:
            for _r, _ in rs:
                if _r < 0:
                    _r += n
                if f(_r) % n == 0:
                    ret.add(_r)

    return tuple(ret)


# test
n = 45649
PR.<x> = PolynomialRing(Zmod(n))

# x = 4
ans = 4
f = x^2 + 113*x + 45181

roots = solve(f, m=3, X=200)

# check
assert ans in roots
for r in roots:
    assert f(r) == 0

余談1: small_roots()との対応 §

SageMathに実装されているCoppersmith's Attackはsmall_roots()という名前だが、これの引数はある程度今回使ったものに対応している。

よく使うのはepsilon, X, betaであるが、beta以外は次のような対応になっている。

  • epislon: 1m\frac 1mに対応する。よって、上記実装でmmを指定するのはepislon=1/mとしているのと同じである
  • X: (明らかだが)XXに対応する

余談2: nnの約数を法とする場合 §

Coppersmith's AttackのVariantとして、未知のnnの約数を法とした場合の合同方程式も解くことが出来るというのがあるが、これは通常のCoppersmith's Attackで多項式にnnを掛けるところをnnの約数に変えているだけである。と言っても、nnの約数は未知なので直接掛けるのではなくnnを掛けて代用している。

そのせいで格子の体積が大きくなり、簡約しても出てくる基底のノルムが大きくなってしまう。よって、上記のXXの評価はそのまま使う事は出来ない。一般のRSA同様n=pqn = pqpqn1/2p\approx q \approx n^{1/2}として、ppを法とした合同方程式を解くのなら、解の大きさはn1/4n^{1/4}未満である必要がある。

気が向いたら解の大きさについての評価を書きます。

Resources §