CTFにおけるCrypto入門とまとめの1つです。
- RSA
- 攻撃手法
- nの素因数分解が出来てしまう
- Low Public-Exponent Attack
- Håstad's Broadcast Attack(同報通信、同一平文の問題, Hastad Broadcast Attack)
- Common Modulus Attack
- Wiener's Attack
- Franklin-Reiter Related Message Attack
- Fermat's Method
- Multi-prime RSA: 3つ以上の素因数からなるRSA
- 復号化時間を正確に取得できる場合はタイミング攻撃が成立するかも
- LSB Decryption Oracle Attack / LSB Oracle Attack
- Coppersmith's Method/Attack
- RSA署名:RSAで公開鍵署名をするとき(秘密鍵で暗号化して、公開鍵で復号化して検証するとき)
- RSA-OAEP
- PKCS #1 v1.5
RSA
- RSA: 素因数分解が困難であることを安全性の根拠とした公開鍵暗号
- 前提となってる数学的なことや性質
- オイラーの定理
- aとnが互いに素であり、φ(n)をオイラーのφ関数とするとき、a^φ(n) ≡ 1 mod nが成り立つ
- phi: オイラーのφ(n), nに対してnと互いに素である1~nの自然数の個数
- φ(n)=φ(pq)=(p-1)(q-1)である
- 他にも
φ(pqr)=(p-1)(q-1)(r-1)、φ(ppqq)=p(p-1)q(q-1)、φ(p^n)=p^(n-1)(p-1)、φ(ppp)=pp(p-1)、φ(pp)=p(p-1)、φ(p^rq)=(p-1)*p**(r-1)*(q-1)みたいな感じ
- 乗法準同型性: RSAの暗号化処理をfとした場合に平文a,bに対してf(ab)=f(a)f(b)が成立
- つまり、任意の平文から暗号文が作れるときに、暗号化したまま既存の平文に乗算できるということ
- これを使って、適応的選択暗号文攻撃が可能
- ちなみに、累乗計算は繰り返し二乗法(バイナリ法)で高速計算している
- オイラーの定理
- 簡単なRSA問題はRsaCtfToolで解けたりする setodaNote CTF Writeup (Crypto) - Tahoo!!
- nが素数のとき(ポーリック・ヘルマン暗号)
- gcd(n-1,e)=1のとき
d = inverse(e,n-1)dをこんな感じで計算する。- つまり
ed = 1 mod (n-1)なのでed = k(n-1) + 1
- つまり
m = pow(c,d,n)復号化は普通- (me)d = med = m^{k(n-1) + 1} = m
- 最後の部分は
m^(n-1) = 1 mod nなのでm^{k(n-1)} = 1となるため
- 最後の部分は
- (me)d = med = m^{k(n-1) + 1} = m
- gcd((n-1)/2,e/2)=1のとき
d = inverse(e//2,(n-1)//2)そのままじゃ逆数計算できないので、2で割って無理矢理やる- つまり
ed/2 = 1 mod {(n-1)/2}なのでed/2 = k{(n-1)/2} + 1でed = k(n-1) + 2
- つまり
mm = pow(c,d,n)とする- mm = (me)d = med = m^{k(n-1) + 2} = m2
- Cipollaのアルゴリズムを使って m2 = x (mod n) でx,nが既知の状態からmを求める
- gcd(n-1,e)=1のとき
- eとphiは互いに素である必要がある
ed = 1 (mod phi(n))を計算する過程で、解が存在しなかったり、複数存在したりするため?いや、そもそもオイラーの定理が成り立たないのか- これを利用して解が本当の解のp倍だったりして良い感じにgcdしたら解ける問題もあった
- 互いに素ではない場合は復号化できないかというとそうでもない
- カーマイケルの定理: オイラーの定理を精緻化した定理。全てのaに対してam=1 mod nが成立するようなmを与える
- これを利用してdを以下のように計算可能 https://github.com/SECCON/Beginners_CTF_2021/blob/main/crypto/p-8RSA/solver/solve.py
X = (p - 1) * (q - 1) // GCD(p - 1, q - 1)L = pow(2, X // e, n)d = inverse(e, X // e)- これを使って cd mod n するか、 cd*Li mod n (0≦i<e)する
- https://y011d4.netlify.app/20201026-not-coprime-e-phi/
- Hack.lu CTF 2020 の Bad Primes
p - 1 = 0 mod eとなっていて、gcd(e,phi(n))=eとなってしまう。こうすると、ed=1 mod phi(n)を満たすdが存在せず、計算ができない。
- https://github.com/srdnlen/srdnlenctf-2022_public/tree/main/crypto_wtfrsa
- https://blog.y011d4.com/20210524-ctf4b-writeup#p-8rsa
- カーマイケルも使えないとき?
- Nが分からないとき
- (-1)eが計算できると、N-1(mod N)が得られるので、+1すればNが得られる
- eが既知で平文と暗号文の組がいくつか得られるときは、
gcd(平文1^e-暗号文1, 平文2^e-暗号文2, 平文3^e-暗号文3, ...)をすればNが得られる平文i^e-暗号文i = 0 mod Nであるため、平文i^e-暗号文iはNの倍数だから- 暗号文の一部がちょっと小さくて、AGCD問題になってこれはLLLで解けますという問題もあった https://hackmd.io/VXBgjljNTKatGeOx1O8v7A?view#%F0%9F%90%BA-Verifier-Max
ed = 1 mod phiであるので、ed = 1 + k * phiということになるが、このときのkはそれほど大きくないので全探索可能だったりする(理屈はよくわかっていないが、min(e,d)くらいの大きさ?)- RSA-CRT
m = c^d mod Nの計算をCRTを使って高速化する手法- Nを素因数分解して
N=p1*p2*...*pnであるとする di = d mod (pi-1)をしてdiを求め、mi = c^di mod piでmiをそれぞれ求める- こうすると、
mi = m mod piになっているので、CRTでp1~pnをまとめるとm mod Nが求まる - Fault Attack https://hackmd.io/@Xornet/ryoP8VxUw
- Nを素因数分解して
攻撃手法
- https://inaz2.hatenablog.com/entry/2016/01/15/011138
- https://www.slideshare.net/sonickun/rsa-n-ssmjp
- RSA暗号攻撃で他でも使える n のこと - Project Euphoria
- セキュリティキャンプ2021 参加記 - Qiita
- http://www.crypto-uni.lu/jscoron/cours/mscrypto/cc3b.pdf
- https://elliptic-shiho.hatenablog.com/entry/2015/11/12/182219
- https://lazzzaro.github.io/2020/05/06/crypto-RSA/
- https://furutsuki.hatenablog.com/entry/2021/03/16/095021
nの素因数分解が出来てしまう
- 生成方式が明らかでないとき、とりあえず自動で素因数分解できないか試す
- factordb.com 一番手軽でやる価値ある
- 2番目に手軽 https://www.alpertron.com.ar/ECM.HTM
- YAFU: ヒントのないRSA問題では試してみる価値あり
- 128-bitくらいの素数なら解ける
- YAFUを使って大きな数を素因数分解してみる - ももいろテクノロジー
- primefac: YAFUでできない素因数分解でもこっちなら出来たりする
- CADO-NFS: 時間とお金を使えばかなりのビット数まで素因数分解できる、コンテストでも成績がいいツール。あまり使うことはない?
- msieve https://qiita.com/motimotipurinn/items/087dedae95a770345132
- sagemathならfactor
- どういったときにできちゃうか https://www.slideshare.net/slideshow/rsa-n-ssmjp/72368516
- ビット数(鍵長)が小さい
- 256bitくらいなら個人PCでも素因数分解可能
- 10進数で700桁ならfactordbで行けた
- p,qの片方が小さい -> 小さいほうで全探索していく
- 近い値の素数p,q -> Fermat's Method
- 素数p,qを有名な素数(メルセンヌ素数など)にしてはいけない
- メルセンヌ素数:2n-1で表現される素数。50個くらいしか発見されていないので素因数を全探索可能
ns = [2,3,5,7,13,17,19,31,61,89,107,127,521,607,1279,2203,2281,3217,4253,4423,9689,9941,11213,19937,21701,23209,44497,86243,110503,132049,216091,756839,859433,1257787,1398269,2976221,3021377,6972593,13466917,20996011,24036583,25964951,30402457,32582657,37156667,42643801,43112609,57885161,74207281,77232917,82589933]# 2n-1がメルセンヌ素数
- フェルマー素数 p=2k+1という形をした素数で、3,5,17,257,65537しか見つかっていない
- メルセンヌ素数:2n-1で表現される素数。50個くらいしか発見されていないので素因数を全探索可能
- 同じ素数を使いまわしてはいけない、2つの異なるNについて同じpが使われている場合
- N1 = pq1, N2 = pq2みたいな感じなら、N1とN2の最大公約数を求めるとpが分かる
- 同じ素数が使われる可能性が高く、かつ、沢山のサンプルを用意できる場合
- ビット数(鍵長)が小さい
- https://wacchoz.hatenablog.com/entry/2019/01/05/230128 素因数分解方法アレコレ
- Pollardのρ法
- ポラードのp-1法: 素因数のうち1つをpとしたときに、p-1の持つ最大の素因数が小さい場合に使える高速な素因数分解
- 前提として、nの素因数のうちの1つがpであり、p-1がB-smoothで、Bがそれほど大きくないとする
- B-smooth: ある整数Nの最大の素因数がB以下
- つまり、B-smoothな数はB以下の素数p1,p2,...,pnを用いて、p1a1 * p2a2 * ... * pkakと書ける
- 以下のMを作ってp-1の倍数である必要があることを考えると、B-smoothであること+倍数になることをちゃんと考慮する必要がありそう?
- B-smooth: ある整数Nの最大の素因数がB以下
- 適当にnと互いに素なaを決める(大体a=2でok)
- p-1の素因数の候補を集めて総積を取りMとする(この時、Mがp-1の倍数になるようにする←ここが一番大事)
- 例えば、AlpacaHack Round 3のRainbow Sweet Alchemistではここで使われていた素数が決定的だったため、候補がかなり絞れた
- https://furutsuki.hatenablog.com/entry/2024/09/15/201136#Rainbow-Sweet-Alchemist では増やして試してを繰り返している。賢い
- gcd(pow(a,M,n)-1,n)を取るとpが出てくる
- Mがp-1の倍数ということは M=k(p-1) と書ける
- aとpが互いに素であることから、フェルマーの小定理によりpow(a,p-1,p)=1
- つまり、pow(a,M,p) = pow(a, k(p-1), p) = 1
- つまり、aM-1 = sp
- よって、gcd(pow(a,M,n)-1,n) = gcd(sp,n) = p となる
- B-powersmooth: ある整数Nを素因数したときのpiaiの最大のがB以下
- 手順
- Bを適当に決めて、B-powersmoothのうち最大の数Mを計算する
- 5-powersmoothのうち最大の数Mは2235
- 素因数分解したい数Nと互いに素な任意の正整数aを用意して、GCD(AM - 1, N)を計算し、1とN以外の最大公約数が得られたらその数が素因数となる
- Bを適当に決めて、B-powersmoothのうち最大の数Mを計算する
- 前提として、nの素因数のうちの1つがpであり、p-1がB-smoothで、Bがそれほど大きくないとする
- ウィリアムズのp+1法
- 楕円曲線法 ECM: 小さい素因数を沢山持つ合成数に対して有効とされる
- フェルマー法
- 2次ふるい法、一般数体ふるい法: CTFでは多分要求されない。現状最速の素因数分解アルゴリズム。2022年では829ビットの素因数分解ができる
- p, q=p+2といった双子素数でp,qが作られている場合
- N = pq = p(p+2) = p2 + 2pとなり、
p^2 + 2p - N = 0を解けばpが分かる
- N = pq = p(p+2) = p2 + 2pとなり、
- nを大きいものにできる場合
- 十分に大きい素数 https://ja.wikipedia.org/wiki/%E5%B7%A8%E5%A4%A7%E3%81%AA%E7%B4%A0%E6%95%B0%E3%81%AE%E4%B8%80%E8%A6%A7
- SECCON Beginners CTF 2022: PrimeParty -> n=pqrs * 入力された素数x
- eとdが分かっていればNを素因数分解可能
- ed=1+k(p-1)(q-1)より(ed-1)/k=(p-1)(q-1)なので、edの値とNの値を比較すればkがどのくらいの大きさか分かり、kが全探索できればしてしまって、ed-1を割り切れるkで(p-1)(q-1)が分かったらpq=Nの式と連立方程式をして、p,qが計算可能
- furutsukiさんのとこにある謎のやり方 https://furutsuki.hatenablog.com/entry/2021/03/16/095021#e-d%E3%81%8C%E3%82%8F%E3%81%8B%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%81%A8%E3%81%8D%E3%81%ABn%E3%82%92%E7%B4%A0%E5%9B%A0%E6%95%B0%E5%88%86%E8%A7%A3%E3%81%99%E3%82%8B
- Factor from random known bits https://github.com/y011d4/factor-from-random-known-bits
- どっかで見たアイデア: p,qが共通の大きなsuffixを持つ場合は、平方根をつかうことでsuffixを見つけることができ、そこからCoppersmithで全体を見つける
- よく、RSAでよく使われる式以外に、p,q,d,phiの関連式の値が追加で与えられるので、p,qを計算してという問題が出てくる -> 数式をガチャガチャしたり、modを弄ったり、全探索したり、Coppersmithして解けたりする
Low Public-Exponent Attack
- eが小さいときに、nのe乗根以下の平文mについては、単純にcのe乗根を取れば平文を求めることができる攻撃。https://blog.akashisn.info/entry/2018/08/07/150018
- me<Nならば(modで値が変化しないので)攻撃可能なので、eだけでなくmが小さい場合も同様に攻撃が可能
- Nowruz1404 - Brutal RSA
- https://github.com/FlagMotori/Nowruz1404/blob/main/crypto/Brutal%20RSA/solve.py
- m3 = c+nkであり、kがそんなに大きくないことを期待して、kを0から増やして3乗根を取って全探索する
Håstad's Broadcast Attack(同報通信、同一平文の問題, Hastad Broadcast Attack)
- m,eが同じでNが異なるメッセージがいくつかある場合、Hastad's broadcast attackという攻撃が可能
c1 = m^e (mod N1)c2 = m^e (mod N2)...というのが与えられているので、中国剰余定理を使うことでmod N1N2...におけるmeが得られて、後はe乗根を取れば平文に戻せる- m<Nであるはずなので、N1,N2とかのbit数を見れば何個のサンプルを使ってcrtしてやればいいか分かる
- 例えば、eが3とかであれば、サンプルを3つ集めればmodが
N1*N2*N3のようになるので、N-1を3乗してもちゃんと収まって解ける。ゴミデータが混ざっていてもサンプル3つを全探索で集めてやればよいという問題であった。
- 例えば、eが3とかであれば、サンプルを3つ集めればmodが
- Wikipediaにある一般化
- https://en.wikipedia.org/wiki/Coppersmith%27s_attack#H%C3%A5stad's_broadcast_attack
- 互いに素な合成数を法とする一変数方程式系
g_i(M)=0 mod Niが十分な数与えられると解ける - CakeCTF 2021 Party Ticket https://blog.y011d4.com/20210829-cakectf-writeup#party-ticket
c=m(m+b) mod nでn,b,cが異なるものが2組与えられるので、共通するmを求める- 最終的にはCoppersmithで解くが、そのためには個々の方程式だとmが十分小さくならないので、方程式を合成してmを十分小さくすることでCoppersmithする(ということだと思う。多分)
gi(m)=m(m+b)-c mod niとして構成したg1(m)=0 mod n1とg2(m)=0 mod n2から合成した、G(m)=0 mod n1n2を作る- そのために、
G(m)=(n1の倍数でなく、n2の倍数)*g1(m) + (n2の倍数でなくn1の倍数)*g2(m)mod n1n2を構成することを考える- これならmod n1、mod n2にすると元の方程式になる
- この倍数を特定するのにCRTを使う
(n2の倍数)=CRT([1, 0], [n1, n2])とすると、n2の倍数だが、n1の倍数ではない数が得られる- y011d4さんのブログにあるように、つまりは、クロネッカーのデルタδijを使って、Ti=δij mod nj for all jとなるTiをmod n1n2で求めて使うことになる
- そのために、
- あとはCoppersmith
- plain RSAに対する攻撃手法を実装してみる - ももいろテクノロジーが単純で読みやすい実装
- 問題
- https://github.com/srdnlen/srdnlenctf-2022_public/tree/main/crypto_easyrsa
- SECCON 2022 BBB https://keymoon.hatenablog.com/entry/2022/11/18/083754#crypto-BBB-50-solves--2209-2356
- 不動点を使い、eを固定化させることでこの問題に帰着させる
Common Modulus Attack
- Nと平文mが一緒で異なるeで暗号化した暗号文cの組がある場合、拡張ユークリッド互除法を用いて平文mを復号可能
- mp+qが分かっている場合、mp-1(q-1)=1であることを使うと、mp+q=mN+1になり、meと合わせるとCommon Modulus Attackができる。gcd(N+1,e)=1
- gcd(e1, e2) > 10の場合は攻撃が難しいらしい
Wiener's Attack
- 1989年にWienerが提唱した、dが小さいことを利用した攻撃
d < N^0.25を満たすときに連分数展開によってNを素因数分解できるというものd < (1/3)*(n^-4)とかいてあるものもある https://note.com/utaka233/n/n088724c6b6c2
- 求め方
de=k(p-1)(q-1)+1を近似していくとde≈knなので、e/n ≈ k/dと変形できるe/nを連分数近似していくと、何処かの近似値がk/dと一致するようになり、k,dが求められる- とあるk,dが正しいかどうかは
de=k(p-1)(q-1)+1から(p-1)(q-1)を求め、n=pqからp,qを求めてp,qが素数であるかを検証すればいい
- とあるk,dが正しいかどうかは
- だが、Coppersmith法を使ったBoneh-Durfee Attackというのがあり、
d < n^0.292を満たすときに使える上位互換- 詳しくはCoppersmithの中で説明
Franklin-Reiter Related Message Attack
- 一般的な形
- 無知:平文m1, m2
- 既知:e, N, 暗号文c1, c2, 関数f(x)
- このとき
m2 = f(m1)であることが分かっている
- このとき
- この時に、m1を高速に計算可能
- 理屈
- 関数f(x)=2x+1であるとする。以下は全部mod Nを省略する
- m1e = c1 であるから g1(x) = xe - c1 とすると g1(m1) = 0
- (2m2+1)e = c2であるから g2(x) = (2x+1)e - c2 とすると g2(m1) = 0
- g1もg2も同じx=m1を根として持つので、
- g1(x) = (x - m1) h1(x)
- g2(x) = (x - m1) h2(x)
- のように共通の多項式を持つ
- よって、g1とg2の公約式を求めることでx - m1の式を得ることができ、m1の値が得られる
- どうやって公約式を求めるか
- ユークリッド互除法でも計算可能
- e=65537では単純なSageの実装で数十分はかかるらしい
- Half GCD: より高速な計算方法
- こっちなら数分で完了する
- 下にsagemath(PARI)での実装がある
- もしくはこんな感じで借りてくる https://m5453.hatenablog.com/entry/2023/04/26/001957#41-Roted-Secret-Analysis-161-pts-34-soleved
- ユークリッド互除法でも計算可能
- https://project-euphoria.dev/blog/27-rsa-attacks/#franklin-reiter-related-message-attack
- 関数f(x)は線形変換である必要はないし、eも共有している必要はない(e1, e2でそれぞれ違っていても良い)
- より一般的には、無知である要素が平文部分のみである方程式が2つ手に入れば良い
- SECCON 2020 CTF - urara https://jsur.in/posts/2020-10-12-seccon-2020-ctf#urara
- EC上の点P(m, 乱数)に対して2P(Qx,Qy)が分かっている状態であれば、
(3m^2+a)^2+4(m^3+am+b)(Qm+2m) = 0であることが分かる - もう一つ
(m+t)^e-c = 0というのが分かっているので、mをxにしてf(x),g(x)を用意してgcdすれば(x-m)という項が出てくるはず
- EC上の点P(m, 乱数)に対して2P(Qx,Qy)が分かっている状態であれば、
- SECCON 2020 CTF - urara https://jsur.in/posts/2020-10-12-seccon-2020-ctf#urara
- e,Nが共有で、2つの平文m, m+dがあったとする。つまり、2つの平文の差が判明している場合
- Franklin-Reiter Related Message Attackという攻撃が成立
- SageMathで解析可能 InterKosenCTF 2020 - padrsa - HackMD
- ちょっと早そうなコード https://github.com/srdnlen/srdnlenctf-2022_public/tree/main/crypto_oneflagpadding
- https://www.yumpu.com/en/document/read/54527505/ctf-crypto-matome
- ここによると、「e,nが同じ、かつ、enc(m)とenc(am+b)であれば攻撃可能らしい」
- m1=m+d1, m2=m+d2でc1=(m+d1)eとc2=(m+d2)e mod Nとなっていて、c1,c2,e,N,d1,d2が既知である状態でも、m'=m+d1とすれば元の形にできるので解ける
Fermat's Method
- 2つの素数に対して
N=pq=(a+b)(a-b)と表現できる- p,qの間が小さい場合、aはNのおおよその平方数となり、bは小さくなる
- よって、Nの平方数周りをaとして採用して、bを全探索すれば素因数分解可能
- フェルマー法 https://tex2e.github.io/blog/crypto/fermat-factorization-method の実装が一番良い
- p,qはnの平方根付近になるので、そのあたりで探索
- nが4092bitsで、pとqの差が512bitsくらいなら直ぐ出てきた
(a+b)(a-b)と表現できて、aが比較的簡単に計算できて、bが全探索可能な大きさであれば何でも適用可能- 2つの合成数に対しても適用可能 https://project-euphoria.dev/blog/19-ccc-2021/#no-stone-left-unturned
11q = 7p + 11xということが分かっていて、11xが520bitsくらいで小さいため11q ≈ 7pであることが分かる- また、11qと7pはどちらも奇数であるため、
(a+b)(a-b)と表現可能 - そして、pとqの積であるNは既知であるため、
11*7*Nに対してフェルマー法が適用でき、11q, 7pが得られたら、それぞれ割り算すればp,qが求まる
- 2つの合成数に対しても適用可能 https://project-euphoria.dev/blog/19-ccc-2021/#no-stone-left-unturned
(a+b+1)(a-b)の場合で作問できそうでは?- 3乗での応用例 SECCON CTF 2022 - CCC
- 式をこねくり回すと、
aN = s^3 - b^3という式が出てくる。条件は以下- a, Nは既知で、
s = ap+b - 大きさはaが23なので5bits, pが1024bits, bが691bits、よってsは1029bits, Nは2058bitsということで
3082bits = 1029bits^3 - 691bits^3という感じなので、b3は半分ほどの大きさしかないため、無視できるほど小さいと見る
- a, Nは既知で、
- 以上の情報から
aN ≈ s^3であると言えるため、sの値はaNの3乗根にとても近い所にあると推測される - よって、aNの3乗根を取り、+0,+1,+2,...+10000をsの値として、
s^3-aNが立方数になるものを探していく- 立方数となれば、a3-aNの3乗根がbとなり、sも既知であるため、pを求めることができる(よって、qも求まり、RSAが解ける)
- 式をこねくり回すと、
- 問題
- RTACTF - Sexy RSA https://blog.y011d4.com/20211219-rtactf-writeup#sexy-rsa
Multi-prime RSA: 3つ以上の素因数からなるRSA
- n=pqrであれば、φ(n)=(p-1)(q-1)(r-1)が分かれば良い
- ちなみに、
(p-1)(q-1)(r-1) = n+(pq+qr+rp)+(p+q+r)-1なので、そっちが分かるのでも良い
- ちなみに、
- Fireshell CTF 2019 Biggars writeup - ふるつき
- sagemathで素因数分解したら3つ以上の素因数へ分解でき、かつ、個数が多い場合はCRTを使って復元していく
復号化時間を正確に取得できる場合はタイミング攻撃が成立するかも
- Side Channel Attack - CTF Wiki EN
- RSA に対するタイミング攻撃: 4 次元で秘密を暴く
- justCTF 2020 - 暗号 | ジョセフのブログ
- DEF CON 2017 Quals - Godzilla (Reverse/Crypto)
LSB Decryption Oracle Attack / LSB Oracle Attack
- 平文を2のx乗した結果が得られるときに、二分探索を使ってmを復元できるテク
- つまり、
(2^1 m mod N) mod 2,(2^2 m mod N) mod 2,(2^3 m mod N) mod 2... が得られるときにmを復元可能 - RSAの暗号文は乗法性があるので、enc(2x)enc(m)ができるので、このテクが使いやすいということだと思う
- つまり、
- アルゴリズム
[L, R) = [0, N)を開始状態とする(2^1 m mod N) mod 2を計算して…- 0ならば、
[L, R) = [L, (L+R)/2) = [0, N/2) - 1ならば、
[L, R) = [(L+R)/2, R) = [N/2, N)
- 0ならば、
(2^2 m mod N) mod 2を計算して…- 0ならば、
[L, R) = [L, (L+R)/2) - 1ならば、
[L, R) = [(L+R)/2, R)
- 0ならば、
- これを無限にやると、範囲の幅が1になって最終的に残る1つがm
- 準同型性があればRSAでなくても適用可能
- Paillier暗号は加法における準同型性を持つので、暗号のまま平文を2倍、4倍、8倍できるので、同様にLSB Oracle Attackが適用できる https://73spica.hatenablog.com/entry/2017/11/16/031602
- 資料
- 問題
- ImaginaryCTF 2025 leaky-rsa https://github.com/ImaginaryCTF/ImaginaryCTF-2025-Challenges/tree/main/Crypto/leaky-rsa-revenge
- 下位1ビットでなくてもシフトを同様に工夫すれば同様に二分探索可能
- 区間を管理するのではなく、0なら+0, 1なら+n/2i+1をしている
- 最終的に出てきたmに若干ずれがあるみたいで、±4の周辺を見てみている
- ImaginaryCTF 2025 leaky-rsa https://github.com/ImaginaryCTF/ImaginaryCTF-2025-Challenges/tree/main/Crypto/leaky-rsa-revenge
Coppersmith's Method/Attack
Coppersmithは「整数方程式」の絶対値の小さな整数解を多項式時間で求める手法。RSA問題の部分問題としてこのような問題がでてくるので、CoppersmithがRSA問題を解くのに使える。RSA問題以外でも良く出てくる。
Coppersmithの歴史
- 1996年 Coppersmithが提案した手法
- 2変数「整数方程式」の絶対値の小さな整数解を多項式時間で求める手法 - Finding a Small Root of a Bivariate Integer Equation; Factoring with High Bits Known
- RSAの秘密鍵導出攻撃ができる。N=pqのpとqの上位半分のビットp',q'が分かっているときに、
(p'+x)(q'+y)-N=0がx,yがN1/4程度なので解ける - 非常に難解なため、Coronによって再定式化された以下の論文での理解がオススメ
- 2004 Finding Small Roots of Bivariate Integer Polynomial Equations Revisited
- Coppersmithの手法より少し劣っていた
- 2007 Finding Small Roots of Bivariate Integer Polynomial Equations: A Direct Approach
- Coppersmithの手法と同等に
- 2004 Finding Small Roots of Bivariate Integer Polynomial Equations Revisited
- https://www.jstage.jst.go.jp/article/bjsiam/18/3/18_KJ00005033174/_pdf 他にも研究が続けられていることが分かる
- RSAの秘密鍵導出攻撃ができる。N=pqのpとqの上位半分のビットp',q'が分かっているときに、
- 1変数整数係数合同方程式の絶対値の小さな整数解を多項式時間で求める手法 - Finding a Small Root of a Univariate Modular Equation
- RSAの平文回復攻撃ができる。平文mの |m-m'|<N1/e上位ビットm'
- 非常に難解のため、Howgrave-Grahamによって再定式化されたより簡潔な手法による説明が一般的らしい
- 1997 Finding Small Roots of Univariate Modular Equations Revisited
- 2変数「整数方程式」の絶対値の小さな整数解を多項式時間で求める手法 - Finding a Small Root of a Bivariate Integer Equation; Factoring with High Bits Known
- 有名問題とCoppersmith: 有名問題をCoppersmithで解くという観点で攻撃に名前がついていたりする
- Wiener's Attack: Coppersmithを利用することで、Wiener's Attackでの前提条件でより良く(解ける範囲が広く)なった
- オリジナルのWiener's Attack
d < N^0.25- ????年 Wiener's Attackを
d < N^0.25という同じ条件下でCoppersmithでも解けることが判明
- ????年 Wiener's Attackを
- 1999年 Boneh-Durfee Attack
- Boneh-Durfeeによるsmall exponent attack:Wiener's Attackの拡張版
2k((N+1)//2-s)+1をmod eでk,sを変数とした多変数多項式のCoppersmithをすればk,sを求めることができる- https://zenn.dev/anko/articles/ctf-crypto-rsa#%E3%81%8C%E5%B0%8F%E3%81%95%E3%81%99%E3%81%8E%E3%81%A6%E3%81%AF%E3%81%84%E3%81%91%E3%81%AA%E3%81%84-(wiener's-attack)
- 弱Boneh-Durfee手法だと
d < N^0.284まで解けた - 強Boneh-Durfee手法だと
d < N^0.292まで解けた
- オリジナルのWiener's Attack
- 部分鍵導出問題: RSAの秘密鍵dの部分的な情報が得られているときにdを復元する問題
d=d1M+d0でMが2の累乗のとき- 2005年: ErnstらがCoppersmithで解いた
- 2003年: BlomerとMayのBlomer-May攻撃
- 2014年: TakayasuとKunihiroによるTakayasu-Kunihiro攻撃が現状最強
- Wiener's Attack: Coppersmithを利用することで、Wiener's Attackでの前提条件でより良く(解ける範囲が広く)なった
最強のCoppersmith's Method Solver - cuso
https://yu212.hatenablog.com/entry/2025/07/27/163347
1変数の場合
- 1変数の合同方程式に関する最も汎用的な形
- Nを既知の整数, モニックな1変数δ次多項式f(x)の答えを知りたい
- モニックとは最高次係数が1であるような多項式
- 0<β≦1を決める
- このとき、f(x') = 0 (mod b) かつ |x'|≦Nβ2/δ を満たすx'を求めることができる
- 計算量はlogN,δの多項式時間
- 計算時間はβの値には依存しない
- 計算量はlogN,δの多項式時間
- 以上のことを考えると、Nは別に大きくてもよさそうだが、式変形などを頑張ってδをなるべく小さくする(多項式の次数を下げる)ことで、計算可能なx'の範囲も広がるし、計算時間も短くなるので嬉しい
- この手法がSagemathのsmall_roots関数やPari/GPのzncoppersmith関数で採用されている
- Nを既知の整数, モニックな1変数δ次多項式f(x)の答えを知りたい
- sagemathのsmall_roots関数
- 使い方
python PR.<x> = PolynomialRing(Zmod(n)) f = e * (s + 2**470 * x) - 1 - k * (-1) * (rq - 1) # 多項式を用意 f = f.monic() # モニック多項式である必要があるので変換 roots = f.small_roots() # 解く ans = int(roots[0]) # intでキャストしないとバグるので注意 small_roots(self, X=None, beta=1.0, epsilon=None)https://github.com/sagemath/sage/blob/e7477f887968f0f133e15618ad5444473aa8fb4b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx#L471- X: 根の上界。つまり、
|x'| < Xを満たすXのこと- 省略されると
X = ceil(1/2 N^{\beta^2/\delta - \epsilon})で計算される - 省略せずに
2^Kbitsみたいにギリギリでちゃんと指定したほうが良さそう X < N^(1/degree)
- 省略されると
- beta: 上で説明しているβ。mod Nで計算したい場合はβ=1とする(つまり省略)。Nの約数modにしたい場合は、ギリギリまで調整する
- epsilon: 「根の近似度」や「誤差」の量を制御するためのパラメータ
- 多変数の多項式の解の近似を求める際に重要です。特に、RSAのような大きな整数問題を扱う場合、ϵ の選び方によって、解がどれだけ正確に求まるか、または計算が成功するかに影響を与えます
- 小さい根を求める場合(小さな公開鍵の因数分解など):ϵ は比較的小さく設定される傾向があります。これは、多項式の小さな根を近似的に求める場合に高い精度が必要であるためです。
- より大きな問題に対する攻撃の場合:ϵ は問題のスケールに応じて調整されることがあります。例えば、整数のサイズが大きくなるほど、適切な近似のために大きめのϵ が必要になることがありますが、攻撃の成功率を維持するためには、依然として小さめに設定することが多いです。
- 一般的には、ϵ は logN (NはRSA modulus)の一部に比例する形で選ばれることが多いです。
- デフォルトは
epsilon = beta/8。epsilon = beta^2/7としているものもある
- パラメタ色々
- 参考 https://qiita.com/ushigai_sub/items/1182d7f49e7ff92646e7#hard-omni-rsa--240points--14solves-
X = 2^42,beta = 0.20,epsylon = 1/16 beta=0.25、epsilon=0.01遅いらしいX=2^42, beta = 0.2X=2**500, beta=0.20https://blog.y011d4.com/20211219-rtactf-writeup#leaky-rsaf.small_roots(X=1<<kbits, beta=0.3)kbitsは変数のbit数(512だった) https://github.com/ptr-yudai/RTACTF-2021/blob/master/crypto/leaky_rsa/solution/solve.sageX=2**500, beta=0.3https://blog.arkark.dev/2021/12/19/rtactf/- 見た感じ(X,beta)の組で指定すればよさそうで、Xは変数の想定bit数を書いて、betaは適当に0.2とか0.3とか色々試す
f.small_roots(epsilon=1/16)epsilonだけ指定する場合。以下のように大きくするほど時間が短縮される。- epsilon=0.033 -> 3m37.348s
- epsilon=1/30 -> 1m52.710s
- epsilon=0.04 -> 1m9.708s
- epsilon=1/16 -> 0m8.615s
- 参考 https://qiita.com/ushigai_sub/items/1182d7f49e7ff92646e7#hard-omni-rsa--240points--14solves-
- sageのsmall_rootsはbetaを適当な値にしたら法nの代わりにnの約数を法とした式をそのまま法nで突っ込んでも正しい答えが得られることがあるっぽい
- nとpののbit数がかなり離れている必要がある?
- X: 根の上界。つまり、
- パラメタガチャ
py for i in trange(1, 101): cur_sol = f.small_roots(X=2**(8*flag_len), beta=i/100)
- 使い方
- テク
- 未知変数は全体に対して小さくないといけないし、次数はある程度小さくないと計算できない。終結式とかグレブナー基底で簡単にするとうまくいくかも?
- 「ここまで次元を落とすことができれば Coppersmith が刺さりそう」https://blog.y011d4.com/20211219-rtactf-writeup#leaky-rsa
- 法は約数modとして考えて答えを出すこともできる
- pが600bits, qが500bitsの素数でn=pqのとき、sq+A=0 mod pからqをmod nから計算できた https://blog.y011d4.com/20211219-rtactf-writeup#leaky-rsa
- 約数modで計算できることを利用して、元々の法の値が分からなくても、因数が共通なら計算できちゃう
- Full Weak Engineer CTF 2025 Multi パワー RSA
- 2乗や3乗のものは扱えないのだが、2乗や3乗してもまだ小さければそれを1つの変数として扱ってしまえる
- Coppersmithの高速化テクがある? Codegate 2024 FactorGame https://blog.y011d4.com/20240602-codegate-writeup#factorgame
- 未知変数は全体に対して小さくないといけないし、次数はある程度小さくないと計算できない。終結式とかグレブナー基底で簡単にするとうまくいくかも?
- Coppersmithに祈りの要素はあまりなさそう
多変数の場合
- 一般に変数の数が増えれば増えるほど解くための条件が厳しくなるため、なるべく変数の数を減らす方向で考察する必要がある
- 多変数の方程式を解くときはdefundのライブラリを使うのが良い
- (あまり理解してない。ガチャ)
初めて勉強する人が読むべき資料
- shihoさんの https://elliptic-shiho.github.io/slide/katagaitai_winter_2018.pdf
- 必ず最初に読むべき。ちゃんと飛ばさずに読むこと
- 特に「方程式を増やすパート」についてはこれ以上分かりやすい説明は多分なく、実際に計算できる問題も付いている
- 2つ目の資料として本「暗号の理論と技術」が良い
- 多変数の場合の話が書いてあるのと、LLLにどんな格子を投げるかについて色々書いてある
- (偉そうに書いているけど、全然理解できてません。cusoでブラックボックス的に解けてしまうので、背景の理論を定期的に忘れる)
- (あと、ちゃんと原論文を読めないとダメなんだと思います)
Coppersmithで解ける問題などなど
- Stereotyped-Message Attack https://qiita.com/ushigai_sub/items/7f6a84b23758e506b3c7#stereotyped-message-attack
- Boneh-Durfee’s Attack https://zenn.dev/anko/articles/ctf-crypto-rsa#%E3%81%8C%E5%B0%8F%E3%81%95%E3%81%99%E3%81%8E%E3%81%A6%E3%81%AF%E3%81%84%E3%81%91%E3%81%AA%E3%81%84-(wiener's-attack)
- Coppersmith's short pad attack -> PKCS1.5を破壊可能
- 2つの平文の差が小さい場合の攻撃方法としてFranklin-Reiter Related Message Attackがあるが、その差も不明の場合にCoppersmith法で求める方法
- SageMathを使ってCoppersmith's Attackをやってみる - ももいろテクノロジー
- Partial Key Exposure Attack
- p,qの片方の素数pの一部分が既に分かっているときにpを復元する手法
- SageMathを使ってCoppersmith's Attackをやってみる - ももいろテクノロジー
- 簡潔な実装例: Connor-McCartney.github.io/Lost-Modulus-Again-HTB.md at 0741bc5d4de56cd51235d084f04133191b75f27c · Connor-McCartney/Connor-McCartney.github.io
- https://zenn.dev/anko/articles/ctf-crypto-rsa#%E7%A7%98%E5%AF%86%E9%8D%B5%E3%81%AF%E9%83%A8%E5%88%86%E7%9A%84%E3%81%AB%E3%81%A7%E3%82%82%E7%9F%A5%E3%82%89%E3%82%8C%E3%81%A6%E3%81%AF%E3%81%84%E3%81%91%E3%81%AA%E3%81%84-(partial-key-exposure-attack)
- SageMathを使ってCoppersmith's Attackをやってみる - ももいろテクノロジー
- ここを見ると派生で色々な攻撃方法がありそう
- Cyber-Security/Notes/crypto_rsa at fed075b1817bebe00aac7dcad26cf632fcfd1d3b · Samdaaman/Cyber-Security
- Coppersmith's attack - Wikipedia
- なんか色々あるのね
- Coppersmith Linear Oneshot
- https://github.com/kionactf/coppersmith/blob/1726e06b9bbaac03d8d075e6ba7417d1b360d5ae/coppersmith_linear.py#L45
- pのhexとして
????????????????????cdf63df9bb0b14828e771e85e2ca9cfb1a638d1ccc554c9a49723a12d1a94697b0bbc64cf8a8e7a12937ee3e0049e6613e6452???????????????????008f897fa4b1c04f4e0a2dc73d66ec0b98c5dddf24e10d6d77923cdf4505204eca2979428cf7184433d4a63f3b77fe7????????????????????みたいに分かっている - pが1024bits, ?のまとまりをa,b,cとすると、aとcが80bits, bが76bitsになっている。この状態でp全体が既知で、上記の部分が分かっていて、a,b,cが未知のとき、Coppersmith Linear Oneshotすると、a,b,cを求めることができる
- 問題
- CakeCTF 2021 Writeup | y011d4.log
- SECCON Beginners CTF 2019 Crypto 解説 - rex-gs
- https://connor-mccartney.github.io/cryptography/small-roots
- RTACTF (SECCON Speedrun Challenge) - Leaky RSA
- https://connor-mccartney.github.io/cryptography/small-roots
- ConnorSmith https://eprint.iacr.org/2012/675 (QnQSec CTF 2025 - myPolyで見た)
- 無茶苦茶信頼度低いが、p,qが512bitsで、d1,d2が440bitsで、e1=d1^-1 mod φ, e2=d2^-1 mod φで、c=(me1)e2 mod Nで、N,e1,e2,cが既知の時に解ける
RSA署名:RSAで公開鍵署名をするとき(秘密鍵で暗号化して、公開鍵で復号化して検証するとき)
https://www.jnsa.org/active/topics/rsa_doc.pdf
- 公開鍵(N,e)で秘密鍵dであったとする
- mを署名するとき、md mod Nを計算して渡す
- もらったmdに対して(md)e mod Nを計算することで復号化して検証する
- ed = 1 (mod phi)なので、ed = 1 * k phiで、フェルマーの小定理よりmphi mod N = 1なので、(md)e = med = m
- パディングについて
- RSAは復号ができても、それが正しく元のメッセージになっているとは限らない
- つまり、メッセージの真正性を検証する必要があり、その用途のためにパディングが用いられる
- パディングをつけて暗号化し、復号化した後にパディングが正しいかを検証することになる
- 署名enc関数のブラックボックス実行環境が与えられているとき、公開鍵Nを特定する方法
gcd(enc(2)^2 - enc(4), enc(3)^2 - enc(9))が公開鍵N- これは
enc(2)^2 - enc(4)がmodNだと0にはなるが、2部分はmodNがかかからないので、ちょうどNの倍数になっている - よって、こういう例をいくつか用意してgcdを取るとNが特定できる
- RSA署名には乗法性がある。f(m)=md mod Nとすると、f(a)f(b)=ad*bd=(ab)d=f(ab) mod N
- RSA-FDH署名: RSA署名にハッシュ関数を組み合わせた方式
- 安全性は向上しているが、効率はあまりよくないらしい
RSA-OAEP
- RSA-OAEP: RSAは選択暗号文攻撃を使って平文の情報がわずかに漏れるらしく、それに対策したもの
- Manger's attack
- RSA-OAEPに対するサイドチャネル攻撃(?)
- OAEPとは平文パディング手法の1つであり、選択平文攻撃に対して強秘匿性を持つ。RSAは一般に暗号文が一意に定まるが、OAEPをパディング手法として使えばランダムになる
- Optimal Asymmetric Encryption Padding - Wikipedia
- 攻撃コード例
- RSA-OAEPに対するサイドチャネル攻撃(?)
PKCS #1 v1.5
- パディング方式 PKCS #1 v1.5 に対する攻撃が色々ある
- パディング方式 PKCS #1 v1.5
00 01 FF ... FF 00 ASN.1 HASHのように平文を作る00 01 ff… ff 00 ‖ HashAlgID ‖ Hash(M)と紹介されている所があるのでASN.1はHashAlgIDを指しているっぽい- ASN.1 is a very complex binary encoding of the hash type and length
- ffの部分の個数は特に決まっていないが、RSAで署名する際にmod Nするのでその時に良い感じにmodが取られるように決める。平文が小さすぎると復元できるのでパディングする
digest = emsa_pkcs1_v15.encode(msg.encode(), 256)の256部分はmodulusのサイズで256bytes分のmodulusでやってますよという意味
- この平文をRSAで署名する。つまり、s = md mod nをする
- 署名検証はRSA署名のやり方と同様で、m = se mod nをして復元してmを確認する
- ASN.1, HashAlgID
- https://www.rfc-editor.org/rfc/rfc3447#page-43
- 'MD5': b'\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'
- 'SHA-1': b'\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'
- 'SHA-256': b'\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'
- 'SHA-384': b'\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'
- 'SHA-512': b'\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'
- Bleichenbacher攻撃がPKCS1.5を壊した。終わり
- その1年前から既に死んでいたらしい? https://discord.com/channels/666188272103325716/666188536638210048/1413493619582701648
- Coppersmith Short Pad
- その1年前から既に死んでいたらしい? https://discord.com/channels/666188272103325716/666188536638210048/1413493619582701648
- BB'98 attack: chosen ciphertext attack against the RSA PKCS#1 encryption standard
- alexandru-dinu/bleichenbacher: Bleichenbacher's PKCS 1.5 Padding Oracle Attack.
- この攻撃が復活してTLSのROBOT攻撃を可能にしている
- PKCS #1 v1.5 メッセージの処理においてパディングエラーが発生した際の動作を観察することにより、暗号文の解読を行う攻撃手法
- Bleichenbacher Attack Explained. Bleichenbacher Attack | by c0D3M | Medium
- 問題
- BB'06 attack: signature forgery attack against the RSA PKCS#1 signature standard
- python-rsaでCVEにもなっている CVE-2016-1494
- 脆弱点
- 公開鍵e=3
- パディングの検証が甘く、復元後の平文にごみを入れ込むことができる
00 01 FF 00 ASN.1 HASH GARBAGEのようにしても検証が通ったり00 01 GARBAGE FF 00 ASN.1 HASHのようにしても検証が通ったり- FiloSottile/BERserk: A Go implementation of the BERserk attack against Mozilla NSS ASN.1 parsing of PKCS#1 RSA signatures with e = 3. Complete of a certificate generation tool, works with CAs in the trust store.
- ASN.1内部にごみを入れ込むことができる例
- できること
- 復元後に任意のprefix, suffixである署名を作成することができる
- prefixについては三乗根をうまく使って作成
- suffixについては下位バイトから貪欲に決めていくヒューリスティックな方法で作成
- ごみを先頭、内部、末尾に追加されてしまうが、それが問題ないなら攻撃可能
- 復元後に任意のprefix, suffixである署名を作成することができる
- 資料
- Bleichenbacher's RSA signature forgery based on implementation error
- 原書っぽい
- python-rsa での Bleichenbacher'06 署名の偽造
- ここが比較的分かりやすい
- Google CTF 2017 Quals - RSA CTF チャレンジの記事 - Trickery Index
- すごくわかりやすそうな見た目をしているが…
- Bleichenbacher's RSA signature forgery based on implementation error
- 実装
- 問題
- 複数の署名が得られていて、nが分からない(求めたい)とき