blog
crypto
このスライドをみてそういえばWiener's Attack実装したこと無いなと思ったので勉強がてら実装してみました.
orisano/owiener
元論文はCryptanalysis of Short RSA Secret Exponentsで
理論についての説明は
http://elliptic-shiho.hatenablog.com/entry/2015/12/18/205804
が詳しいです.
ここでは実装について書いていこうと思います.
Wiener's Attackは連分数展開と連分数から近似分数を求める2つのパートからなります.
連分数展開に関してはユークリッドの互除法の過程で得られるので素直に実装します.
ユークリッドの互除法の計算量は
def rational_to_contfrac(x: int, y: int) -> Iterator[int]:
while y:
a = x // y
yield a
x, y = y, x - a * y
連分数から近似分数を求めるパートがあるのですが,まず連分数から有理数を復元するところから説明します.
連分数を後ろから足して逆数求めることを繰り返していけば復元できます.
def contfrac_to_rational(contfrac: List[int]) -> Tuple[int, int]:
from functools import reduce
return reduce(
lambda f,q: (q * f[0] + f[1], f[0]),
reversed(contfrac),
(1, 0),
)
ちなみに上記のコードは使ってないのでrepositoryにはありません.
前から復元する場合は元論文の式6を用いるとできます.実際にはこちらを使います.
式からもわかる様に前の2つの状態を持っていれば次の状態を作ることができます.
このままだと
def contfrac_to_rational_iter(contfrac: Iterable[int]) -> Iterator[Tuple[int, int]]:
n0, d0 = 0, 1
n1, d1 = 1, 0
for q in contfrac:
n = q * n1 + n0
d = q * d1 + d0
yield n, d
n0, d0 = n1, d1
n1, d1 = n, d
計算量は
用途を考えてgeneratorとして返しています.
近似分数を列挙するのですが元論文の3章のアルゴリズムを用います.
そんなに難しくないので読んでみることをおすすめします.
基本的には連分数を前の方だけ使って復元した値を近似分数とします.
愚直に実装すると以下の様になります.
def convergents_from_contfrac_naive(contfrac: List[int]) -> Iterator[Tuple[int, int]]:
m = len(contfrac)
for i in range(m):
if i % 2 == 0:
q = contfrac[:i+1]
q[i] += 1
yield contfrac_to_rational(q)
else:
q = contfrac[:i+1]
yield contfrac_to_rational(q)
添字が偶数のときは最後の要素を1足した物を有理数に復元し,それを近似分数とします.
これは毎回contfrac_to_rationalを呼んでいて無駄です.
偶数のときの処理が面倒くさい様に見えるが,式を見てみると最後の要素に1を足すという処理は1つ前の結果を足すこと同様だと言うことがわかります.
つまり直前の結果だけ持っていれば良いです.
def convergents_from_contfrac(contfrac: Iterable[int]) -> Iterator[Tuple[int, int]]:
n_, d_ = 1, 0
for i, (n, d) in enumerate(contfrac_to_rational_iter(contfrac)):
if i % 2 == 0:
yield n + n_, d + d_
else:
yield n, d
n_, d_ = n, d
これまで紹介して来たコードを組み合わせてattack関数を定義します.
def attack(e: int, n: int) -> Optional[int]:
f_ = rational_to_contfrac(e, n)
for k, dg in convergents_from_contfrac(f_):
edg = e * dg
phi = edg // k
x = n - phi + 1
if x % 2 == 0 and is_perfect_square((x // 2) ** 2 - n):
g = edg - phi * k
return dg // g
return None
近似分数を総当りでdを特定します.
正しい有理数に復元できたかどうかの判定は元論文のとおりに実装しました.
平方数の判定は
比較的簡単にWiener's Attackを実装できました.
証明やどういう場合に使えるかについては元論文を読んで見てください.
平方数の判定は2分探索を用いることで出来ますが,今回はgmpの実装を参考にしました.
平方数の
全体の
残りの
これにより全体の
残りの
def is_perfect_square(n: int) -> bool:
sq_mod256 = (1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0)
if sq_mod256[n & 0xff] == 0:
return False
mt = (
(9, (1,1,0,0,1,0,0,1,0)),
(5, (1,1,0,0,1)),
(7, (1,1,1,0,1,0,0)),
(13, (1,1,0,1,1,0,0,0,0,1,1,0,1)),
(17, (1,1,1,0,1,0,0,0,1,1,0,0,0,1,0,1,1))
)
a = n % (9 * 5 * 7 * 13 * 17)
if any(t[a % m] == 0 for m, t in mt):
return False
return isqrt(n) ** 2 == n
整数のみを扱うニュートン法を用いました.
ニュートン法は初期値を決めないといけませんが平方根を求める場合は簡単にある程度良い初期値が与えられます.
適切に
def isqrt(n: int) -> int:
if n == 0:
return 0
x = 2 ** ((n.bit_length() + 1) // 2)
while True:
y = (x + n // x) // 2
if y >= x:
return x
x = y
Pythonなりにわかりやすく高速に実装できたかなと思います.
(JavaのBigIntegerを使って実装している例があったのですが読みづらくて辛かった)
実装してみて楽しかったので他の手法も論文を読みながら実装できるくらい頭良くなりたい.
Cryptanalysis of Short RSA Secret Exponents:
https://www.cits.ruhr-uni-bochum.de/imperia/md/content/may/krypto2ss08/shortsecretexponents.pdf
公開鍵暗号 - RSA - Wiener's Attack - ₍₍ (ง ˘ω˘ )ว ⁾⁾ < 暗号楽しいです:
http://elliptic-shiho.hatenablog.com/entry/2015/12/18/205804
pablocelayes/rsa-wiener-attack:
https://github.com/pablocelayes/rsa-wiener-attack
wihoho/Wiener-s-Attack:
https://github.com/wihoho/Wiener-s-Attack
平方数かどうかを高速に判定する方法 - hnwの日記
http://d.hatena.ne.jp/hnw/20140503
Integer square root:
https://en.wikipedia.org/wiki/Integer_square_root
Methods of computing square roots:
https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Rough_estimation