CTFにおけるCrypto入門とまとめの1つです。
本稿は数学科の人間が書いたものではないため、雰囲気をつかむ数学娯楽読本として楽しむこと。(恥知らずにも、数学のことを書くときはなんとなく予防線張っちゃうのですが...)
Cryptoにおける数学
- 高校数学までは完了しているものとして話を進める(CTFer、中高生もそれなりにいるけど...)
- 数学の世界広大すぎるので基本的にはトップダウンで(問題で必要になったら)勉強するで良いと思うが、全体地図を持っていた方がイメージが付きやすいと思うので書いておく
- 全体地図
初等整数論
- フェルマーの二平方定理: 奇素数 p が p=1 mod 4であれば、p=x2+y2 を満たすx,yが存在する
- ユークリッドの互除法
- 拡張ユークリッド互除法 xgcd
- ウィルソンの定理 素数pに対して
(p-1)!=-1 mod p - オイラーの定理
- フェルマーの小定理
- 素数と素数の間は、素数定理により、素数定理より、平均的に
P(n)−n∼O(logn)であり、そんなに離れてないことが利用可能log_2(x)で計算すれば幅が分かる
モジュラー整数
- modを外したいときは
f(x)=0 mod qならばf(x)+kq = 0のようにkを使って外す- 実はkの値全探索できるかも -> RSAとかでたまに見るイメージ
- 複数の方程式のk実は全部一緒かも -> https://su-team.cn/posts/a92a3cc3.html#deez-errors
- X mod Yでgcd(X,Y)=1であれば、X^-1 mod Yは求められる
- 数式が与えられてmodulusを求める問題
- 0 mod pとなる式を二つ作りgcdを取る corCTF 2022 writeup - Attack All Around
x = (x * a + b) % pのような変換を何回も繰り返して結果を予測する- 与式を式変形してxを求めれば、計算を行っても値が変わらないような入力を用意できる(つまり、変換後の値を予測可能になる)
- corCTF 2022 writeup - Attack All Aroundのluckyguess
- 線形合同式を何回か通した式は、離散対数問題として通した回数が復元可能
- corCTF 2022 writeup - Attack All Aroundのexchanged
- corCTF 2022 - Connor Mの式変形が最も易しい
- 式変形を繰り返して
既知^n ≡ 既知 mod pの状態にもっていく。あとは離散対数問題を解けばnが分かる。p-1がsmooth(?)であればPohlig-Hellman algorithmを使えば高速にnが求められる。- p-1を素因数分解してみると、小さい素数のみで構成されていたので、Pohlig-Hellman algorithmを使えそうです。とのこと corCTF 2022 writeup - Attack All Around
- 離散対数問題はsageで解ける
- 不明なパラメタが全探索できないか?
- randとかで少ない範囲であれば全探索で候補を絞り込めるかも
- logを取るのは一般にムズイ -> 離散対数問題
- x mod p, x mod qが分かればx mod pqは中国剰余定理で求められる
- 計算の高速化を要求する問題
- 再帰関数を通す必要があるが、よく見るととある行列計算に帰着させることができ、繰り返し二乗法で高速化
- 素数pの二項係数は1じゃなかったらpの倍数なので…
- (x+y)p ≡ xp + yp (mod p)
- ミラーラビン素数判定法でfalse positiveを出す数は作れる
- https://gist.github.com/keltecc/b5fbd533d2f203e810b43c26ff9d17cc
- 適当に「break millerrabin」でググる
- 例:99597527340020670697596886062721977401836948352586238797499761849061796816245727295797460642211895009946326533856101876592304488359235447755504083536903673408562244316363452203072868521183142694959128745107323188995740668134018742165409361423628304730379121574707411453909999845745038957688998441109092021094925758212635651445626620045726265831347783805945477368631216031783484978212374792517000073275125176790602508815912876763504846656547041590967709195413101791490627310943998497788944526663960420235802025853374061708569334400472016398343229556656720912631463470998180176325607452843441554359644313713952036867
- 未知p,qであり、N=pqとpに関する式が与えられているとき、何とかしてpに関する式から「ほにゃらら=p×なんか」を生み出すことでgcd(N,ほにゃらら)=pを求める
- Approximate GCD Problem
- 連分数でも解けるっぽい?(そっちが想定解法が多い?)
- Buckeye CTF 2022 Quad prime RSA
- Approximate GCDとして解く問題 - RCTF 2021 Uncommon Factor 2 / Midnightsun CTF 2021 Finals Flåarb.tar.xz writeup - ふるつき
- RTACTF 2021 Crypto並走記録
mod 2^mテク:これで変数の下位bitの候補が全探索可能かも。m=1から初めて、最下位ビットを特定したら、m=2,3,...と増やしながら下位ビットから特定していく https://furutsuki.hatenablog.com/entry/2022/06/05/152549- 例えば、mod 2470するといい感じの式が出てきて変数が1個だけだったら、そこからmod21とかmod22にすることは可能なので、その変数の下位ビットから特定することができる
- どっちもありな場合が出てきたりするが、それで進めるとすぐ矛盾するので、枝刈り全探索でいける
- 例えば、mod 2470するといい感じの式が出てきて変数が1個だけだったら、そこからmod21とかmod22にすることは可能なので、その変数の下位ビットから特定することができる
- p-1がAの倍数であれば、mod pで1のA乗根が存在する
- オイラーの定理より xp-1=1 であるため、p-1がAの倍数であれば割り切ることができて、x(p-1)/Aが1のA乗根となる
- https://koba-e964.hatenablog.com/entry/2024/11/01/103550
- G := 1のA乗根であるとき、(1 + G + G2 + ... + GA-1) = (GA - 1) / (G - 1) となり、GAは1なので全体は0になる
- (1 + Gn + G2n + ... + Gn(A-1))も同様にになる
- mod N上で割るときはpowの-1乗を使うこと
/ aは* pow(a, -1, N) pow(-1,e,n)=n-1- 法の変換テク
mod pqの値からmod pの値を求める、つまり、因数modを計算すると元の値からのmodでちゃんと計算できるm^e mod nで何度もeとnが異なるものを取得可能で、確率的にnに小さい素因数が含まれるとき、nで小さい素因数pが手に入ったら、m^e mod nからm^e mod pを計算し、どこからm mod pを計算することで、これを何度も取得し、最終的にCRTでmを求める問題があった
mod pqrで取ったものをmod pqと解釈して解く。なんか色々良い感じになって問題ない場合もある(?)- SECCON 2024 Qual - pqpq
- https://imp.ress.me/blog/2022-11-13/seccon-ctf-2022/#pqpq を見るとそういう話ではなさそう?
mod Nから中身は全く同じ状態でmod pにして式を変形して更にmod Nに戻しても問題ない場合がある(うまくいきそうな雰囲気も感じられるが、うまくいくかどうかはケースバイケースな気がする)
- n乗根
c = m^n mod pms = GF(p)(c).nth_root(e, all=True)で配列で複数候補出てくるので、全部試すmod(c, p).nth_root(e, all=True)- 違いは分からん
- mod 合成数でのe乗根を取りたい場合は、合成数が素因数分解可能であれば、分解してそれぞれe乗根を取ってCRTする
python (sagemath) m1b_ps = GF(p)(m1be).nth_root(e, all=True) m1b_qs = GF(q)(m1be).nth_root(e, all=True) for m1b_p in m1b_ps: for m1b_q in m1b_qs: m1b = crt([int(m1b_p), int(m1b_q)], [p,q]) print(f"{m1b=}")- 単純なものなら
mod(m1be, n).nth_root(e)でもいいっぽい?- nが素因数分解済みならば、
pari.addprimes(p)のように既知の素数を予め与えておくことで計算可能にできる - (p−1)(q−1) がeで割り切れる場合、mod nにおける e 乗根は複数ありえます
- https://chocorusk.hatenablog.com/entry/2025/01/26/180123
- なぜかと言うと...
- mod nをするために、mod pとmod qを計算してCRTをすることで答えを求める
- この時に、mod pを考え、また、p-1がeで割り切れるときは
- m = c1/eであるとき、(mr^{k(p-1)})^{1/e}もまた答えになるため
- (
r^{k(p-1)}=1であるから?)
- nが素因数分解済みならば、
- 単純なものなら
- 「mod 偶数」の場合は最下位ビットの情報が計算によっていい感じに保存されるかも
- zer0pts CTF 2020 - ROR
m^e mod 偶数の場合、「mの最下位ビット = meの最下位ビット」になる
- 逆に奇数の場合
- 2倍すると、modで切り捨てされないと偶数になり、modで切り捨てされると奇数になる
- zer0pts CTF 2020 - ROR
- mod 2、mod 22、mod 23、…でp,qとかを下位ビットから埋めていけないか
- +,*はmod 2iしてもOKで、ビット演算もmod 2iしてもOK。ビット演算を見たら、この方針を怪しんでもいいかも
- 下から埋めていくときに一意に定まらなくてもBFSみたいに候補を持ち越してやっていけば良い感じになる問題もあった https://magicfrank00.github.io/writeups/writeups/trxctf/trxctf-crypto/#factordbcom---writeup
- 2重modの扱い方
- 約数とかでない場合は、補助変数kを使って展開してLLLなり頑張るのかも
P(x) mod m1 mod m2 = rであるなら、k1, k2を導入して、P(x) - k1*m1 - k2*m2 = rと書き直してよいかも(m1 > m2の場合のみ?)- このとき、k2の大きさは大体m1/m2になる
- 約数とかでない場合は、補助変数kを使って展開してLLLなり頑張るのかも
- 分数になると数が大きくなるから違う
- idekCTF 2025 - Catch https://giapppp.github.io/posts/idekctf-2025/
- Fp上で行列を計算していて、計算の逆計算をしていく問題。計算過程で使われる値はpよりも格段に小さい値であるという性質がある
- このとき、割る順番が間違っていると計算結果が分数になり、分数になるということはmod計算で整数値に変換され、[0,p)のどれかになって大きい値になる可能性が高い。もし、割る順番があっていれば分数にはならないので、計算過程の小さい値が保たれる。よって、試し割してみて、数が大きくなるかどうかで正しいもので割れているかを判定できる
- idekCTF 2025 - Catch https://giapppp.github.io/posts/idekctf-2025/
- mod計算を伝搬させていくと、不明変数とのbits差が小さくて全探索できる
phi mod 2**500が分かっていてp,qが512bitsならば、phi=(p-1)(q-1)なのでp+q=N-phi+1とすることでp+q mod 2**500が求められる、p,qが512bitsなのでp+qは513bitsで差が13bitsなので全探索して、方程式の計算ができれば答えが得られるという方針- p+q,pqに解があるかを判定するには 判別式 D = (p+q)2 - 4pq が完全平方数であるかを確認すればよい
- modテク
pの倍数 mod pの倍数はpの倍数になるp^e mod pqは法をpqとしたときにpの倍数 https://blog.y011d4.com/20201227-harekaze-mini-ctf-writeup#rsa
rCi=0 mod rなので、n=pqrでmod nのとき、(p+q)r=pr+qr- フェルマーの小定理 mod r だと pr=p
- 指数計算が重いとき
- カーマイケルの定理 https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A7%E3%83%AB%E3%83%9E%E3%83%BC%E3%81%AE%E5%B0%8F%E5%AE%9A%E7%90%86#%E3%82%AB%E3%83%BC%E3%83%9E%E3%82%A4%E3%82%B1%E3%83%AB%E3%81%AE%E5%AE%9A%E7%90%86
- 全てのaに対して、am=1 mod nを満たす最小のmを求める。オイラーの定理より、m=φ(n)のときam=1 mod nであることを示しているが、この時のmが最小であるとは限らない
- n=pqならば、am=1 mod nを満たす最小のmは、m=lcm(p-1,q-1)=(p-1)(q-1)/gcd(p-1,q-1)
- Ong-Schnorr-Shamir digital signature
x^2-Dy^2=k mod NでD,k,Nが既知であるときにx,yが求められる- redpwnCTF Writeup - retrosign https://blog.y011d4.com/20210713-redpwnctf-writeup#retrosign
- 「mod 乱数」が与えられるとき、乱数は2とか3とかの倍数になりやすいので、「mod 2」とかできるかも
- 「mod 乱数」の数が大量に与えられるので、mod 2,3,5,7,11,...とかをして最頻値を取ると確率的に正しいmod 2,3,5,7,11,...が得られるのでCRTして元々の数を復元する問題がでた - CrewCTF 2025 - inverse with error only
- 問題
- CakeCTF Together as one https://blog.y011d4.com/20210829-cakectf-writeup#together-as-one
- modガチャガチャ
- n=pqのとき、
(ap+bq)^e mod n = (ap)^e + (bq)^e - https://github.com/IC3lemon/CTF-reports/tree/main/BITSCTF-2025/crypto/Noob%20RSA
- https://github.com/tsg-ut/tsg-live-ctf-14/tree/main/crypto/unnecessary_thing_2
- SECCON Beginners CTF 2022 omni-RSA https://blog.y011d4.com/20220605-ctf4b-writeup#omni-rsa
- ed=1 mod (p-1)(q-1)(r-1)のときe( d%(q-1)(r-1) )=1 mod (q-1)(r-1)
- n=pqのとき、
- mod計算で使えるやつ
- a < pであれば、
a % (pq) = a % pのようにpだけの余りにできる
- a < pであれば、
平方剰余: Quadratic Residue
- mod上で平方根を取る
- 素数pと判定したい値aについて、ルジャンドル記号 (a/p) = a**{(p-1)\2} mod p
- 平方剰余の関係式
- 平方剰余 × 平方剰余, 平方非剰余 × 平方非剰余 -> 平方剰余
- 平方剰余 × 平方非剰余 -> 平方非剰余
- これはルジャンドル記号から分かる
- ちなみにaが平方剰余であれば-aは平方非剰余(逆も然り)
- 大体
mod(a, p).sqrt(all=True)で計算可能q- pが2か2のべき乗であるときは頑張る必要がある https://zenn.dev/kurenaif/articles/400a74f51bac31
- pが奇素数、または、そのべき乗
mod(a, p).sqrt(all=True)でok- p = 4k+3であれば、
|x^(1/2)| = x^((p+1)/4)で簡単に計算可能。±の候補があるので注意
- p = 4k+3であれば、
- 合成数の場合は、素数のべき乗に分解して
mod(a, p).sqrt(all=True)して、CRTする
- 解は複数存在する可能性がある
- 平方剰余問題は難しい問題であるが、難しさは素因数分解に相当する
- mod上での平方根の計算アルゴリズム
- p = 4k+3 -> 、フェルマーの小定理を使って
|x^(1/2)| = x^((p+1)/4)と簡単に計算可能 - p = 4k+1 -> トネリ・シャンクス法
- Tonelli-Shanksのアルゴリズム https://37zigen.com/tonelli-shanks-algorithm/
- トネリ・シャンクス法は楕円曲線座標を求めるのに使われるらしい?
- p = 4k+3 -> 、フェルマーの小定理を使って
- x2 = -1 (mod p)であるxが存在するか確認したい -> オイラーの規準を使って平方剰余の存在を確認する https://b1ue.x0.com/writeup/2023zer0pts/#easy_factoring
- sagemath
F = GF(p)として- 平方剰余であるかを判定
F(a).is_square() - 平方根計算
F(a).sqrt(all=True)- p = 4k+1の場合は数分かかるので注意
- 平方剰余であるかを判定
- Rabin暗号: mod Nで平方根を求めるのが困難な平方剰余を数学的背景としている暗号
- Goldwasser-Micali暗号方式
- 平方剰余であるかの判定問題の難しさを前提とした暗号方式
- 任意の整数 N=pqに対して、整数xがNの下で平方剰余であるかどうかを判定することが難しい
- だが、p,qに素因数分解できている状態であれば易しい
- 鍵生成:
- 暗号化:以下に見てわかるように1bitずつ暗号化を行う
- 暗号化したいビットbを選ぶ
- rをランダムに選ぶ (1≦r<N, かつ gcd(r,N)=1)
- b=0なら r2 mod N、b=1なら r2y mod Nを暗号として選ぶ
- 復号化:暗号文cに対して、p,qを用いてNを法として平方剰余か判定して0/1に復元する
- この暗号方式は実用的ではないが、セマンティックセキュリティという暗号文から平文に関するいかなる情報も得ることができないことを保証したものであり、その点で学術的に面白く、革新的であった(らしい)
- 平方剰余であるかの判定問題の難しさを前提とした暗号方式
連分数展開 / 連分数近似
- 既知の有理数a/b, 未知の有理数c/dがあり、a/b ≈ c/d である、つまりとても近い値であるときに、c/dを連分数近似で求められるかも
- https://note.com/utaka233/n/n088724c6b6c2
- a/bをどんどん連分数展開していくと、全体の値が求めたい数にどんどん近似されていく
- その過程のどこかにc/dが出てくるというもの
- RTACTF - Neighber RSA https://xagawa.hatenablog.com/entry/2021/12/20/232133#Leaky-RSA-%E7%9B%AE%E6%A8%99-1800sec
- https://shadowacademy.web.fc2.com/continuedfraction.html
- https://triodelzx.github.io/2024/03/09/%E8%BF%9E%E5%88%86%E6%95%B0%EF%BC%88Continued-Fractions%EF%BC%89%E7%AC%94%E8%AE%B0/
線形代数学
- ベクトル空間: 次の4つの公理を満たす体F上の集合V
- 行列のRank
- 行ベクトルの線形独立なものの最大個数
- 行列を吐き出し法を使って、対角線上に1を並べていったときの1の個数
- 行フルランク = 行列の全ての行が線形独立
- 正方行列における離散対数問題
- xornetさんの解説が最も分かりやすい https://project-euphoria.dev/blog/16-matrix/
- 素数pでFp上の行列を考えた時、A=GkでAとGが既知であるときkを求める離散対数問題を考える
- 対角化可能な場合 (P'=P^-1と書く)
- Gが対角化可能ならば、
- G=PBP'なので、Gk = PBkP' = Aとなるので、
B^k=P'AP=Cで計算可能 - Bは対角行列なので、Bkは対角成分が固有値のk乗である対角行列になる
- Bk=Cであることから、対角成分それぞれについて
b^k=cが得られ、普通の離散対数問題を小問題として得ることができ、そこから、普通の離散対数問題に対する計算テクが適用できる
- G=PBP'なので、Gk = PBkP' = Aとなるので、
- どちらかというと対角化可能であれば、普通の離散対数問題に帰着させるしかなく、解くのが大変
- つまり、こういう場合は対角化可能な行列を選択しなくてはいけない
- Gが対角化可能ならば、
- 対角化できないが、ジョルダン標準形に変換できる場合 -> pに制約が無くても解ける可能性がでてくる
- ジョルダンブロックの累乗を色々変形すると離散対数問題が解ける -> Union CTF - neo-classical key exchange
- どっちもできない場合は、ジョルダン標準形は正方行列に必ず存在するので無し
- ちなみに行列を使った暗号は Non-commutative cryptography の1つ
- 非可換な代数を使ったもの。行列計算は非可換
- Union CTF - neo-classical key exchange https://project-euphoria.dev/blog/16-matrix/
- Ak=BでA,Bが既知であるときにkを求めよと言う問題で、Aが対角化できないとする
- 2x2のとき https://crypto.stackexchange.com/questions/3840/a-discrete-log-like-problem-with-matrices-given-ak-x-find-k
A = PJP^(-1)であり、J = [ [λ 1] [0 λ] ]とする- このとき、
A^k = PJ^kP^(-1)であり、J^k = [ [λ^k kλ^(k-1)] [0 λ^k] ]となる - 式変形をすると
J^k = P^(-1)BP=Cであり、C = [ [a b] [c d] ]とすると、以下のようにkが求まる ``` λk = a kλk-1 = b
kλk = bλ ka = bλ k = b/aλ ```
- 5x5にしたのが今回の問題。手法は殆ど一緒
- GDG Algiers CTF writeup | kanon
- 離散対数問題は基本的に解くのは難関だが、ある条件下において容易化する。今回の場合それに当てはまり、ジョルダン標準形を用いて簡単に行う
- SECCON 2023 Finals - KEX 4.0 https://qiita.com/saitenntaisei/items/5f9caa9110fe38edbc82
- https://lazzzaro.github.io/2020/05/07/crypto-%E7%A6%BB%E6%95%A3%E5%AF%B9%E6%95%B0/
- xornetさんの解説が最も分かりやすい https://project-euphoria.dev/blog/16-matrix/
- 行列の逆行列
- 正方行列、かつ正則の場合 -> sagemath
M.inverse()O(n3)らしい - 非正方行列の場合 -> sagemath
As.pseudoinverse()O(n2m+m2n)らしい - (AB)-1 = B-1A-1
- 正方行列、かつ正則の場合 -> sagemath
- 行列と乗法におけるorder
- 例えば、正方行列Mがあり、
tn=M^n t0のようにMをn乗して計算するようなものが合った場合に、Mの乗法上の位数をM.multiplicative_order()と計算することで、tn=M^(n % M.multiplicative_order()) t0のように計算を簡略化できるかも https://blog.y011d4.com/20220926-ductf-writeup#time-locked A.list()行列を配列に変換
- 例えば、正方行列Mがあり、
- 上三角行列ってe乗すると、対角成分は元の行列の対角成分をe乗した値になる
- ベクトル計算を行列計算にしてみる - SECCON CTF 2023 Quals - RSA 4.0
- 行列の累乗は対角化を行うことで簡潔に表現できるようになるかも
- 行列Aが対角化可能 -> A=PBP-1
- ジョルダン標準形: 任意の行列Aについて、P-1AP=Jと変形することで得られるJのことで、任意の正方行列に対して存在する
- ジョルダンブロック: 対角成分に同じ値 λ を並べ、その1つ上の部分には1を並べた行列
- Jはジョルダンブロックを対角に並べた行列。仮に、全部1x1のジョルダンブロックであれば、対角行列になる
- 逆にジョルダン標準形にしたときに対角行列になっているかどうかで対角化できるかどうかを判別可能
- ジョルダン標準形を使えば、行列離散対数問題はすぐ解ける? https://triodelzx.github.io/2024/07/12/%E8%8B%A5%E5%B0%94%E5%BD%93%E6%A0%87%E5%87%86%E5%9E%8B%E4%B8%8E%E7%9F%A9%E9%98%B5%E7%A6%BB%E6%95%A3%E5%AF%B9%E6%95%B0%E9%97%AE%E9%A2%98/
- 未知変数がn個のときはn個の方程式があれば解ける
- Ax = bのとき、行をn個まで削減してもxが解ける https://gmo-cybersecurity.com/blog/ierae-ctf-2025-writeup-crypto/
- エルミート標準形 -> LLLでちょっと出てきた
- LU分解 S=LU
代数学
どれがどれに属すのかよく分かってないので全部横並びで書いておく。
群 Group
- 群 Group: 乗法あるいは加法が定義されていて、次の4つの公理を満たす集合Gを群という。+or×が定義されていると見ることもできる
- 集合Gの任意の元a,bについて、ab=cとなるような元cが集合Gに存在する
結合則集合Gの任意の元a,b,cについて、a(bc)=(ab)c単位元のe存在集合Gの任意の元aについて、ae=ea=aとなる単位元eが集合Gに存在する逆元の存在集合Gの任意の元aについて、ab=ba=eとなる元bが集合Gに存在する
- アーベル群、可換群:群 + 交換法則が成り立つ
- 巡回群G: 0以外のすべての元がαのべき乗で表される群。αを原始元・生成元と呼ぶ
- 部分群: 群Gの空でない部分集合Hが、群Gと同じ二項演算によって群になっているとき、HはGの部分群と言う
- 2つの位数 order
- 群G1,G2について写像f:G1→G2が準同型写像であるとは、全てのx,y∈G1に対して
f(x*y)=f(x)・f(y)が成立するときを言う(*はG1の演算で、・はG2の演算)- 核
ker(Φ) = {g∈G1|Φ(g)=e2}G2の単位元に写るG1の元の集合
- 核
- ラグランジュの定理 有限群 G の任意の元の位数は群 G の位数の約数になる
- 対称群: ものの並び替え操作を写像とした群
- 位数は要素数をnとするとn!になる。123...nでなくてもlcm(1,2,3,...,n)でも上手くいくらしい
- 置換群 PermutationGroup
- https://pastebin.com/RcD816vd 前半に問題、後半に答え。 Grey Cat The Flag 2025 - Not A Permutation Matrix
- 同型変換して何かしているがさっぱり分からない
- https://pastebin.com/RcD816vd 前半に問題、後半に答え。 Grey Cat The Flag 2025 - Not A Permutation Matrix
- 平方数ではない正の整数mに対して、ペル方程式 x2-my2=1 の整数解には群構造が入る
- 適切に演算を定めると(x,y)*(x,y)->(x,y)で https://math-notes.info/wp-content/uploads/2022/01/group-1.pdf
- https://giapppp.github.io/posts/wannagame-championship-2023/#ezcurve
- ChatGPTより
u**2-kv**2=1(modp)のとき、s**2=kとしてΦ(x,y)=x+syと表現できる- s∈Fpである(kが平方剰余)ならば、
Φ(x,y)∈Fp(計算できる) - s!∈Fpである(kが平方非剰余)ならば、
Φ(x,y)∈Fp^2(計算できない。sを変数として拡大体Fp2上で表現する)
- s∈Fpである(kが平方剰余)ならば、
- ノルムが1であることが重要らしい
- https://hackmd.io/@Giapppp/SJ8Pd8FSa
- https://github.com/thecrabsterchief/my-ctf-challenges/tree/main/wannagame-championship-2023/ezCurve
環 Rign
- 環 Rign: 乗法および加法が定義され、次の4つの公理を満たす集合R。逆元の存在は要求しないので、+,ー,×が定義されていると見ることもできる
- イデアル: 以下の性質を満たすとある環Rの部分集合I
- 多項式の剰余類環
- 与えられた多項式f(x)に対し、f(x)|xe+1を満たす最小のeを多項式f(x)の周期という
- f(x)|xeは、f(x)はxeの倍数という意味
- gcd: 既約が定義できる整域ではgcdが定義できる? -> 整数環、多項式環?
体 Field
- 体 Field: 乗法に関する単位元が存在し、任意の非零元に対し、乗法に関する逆元が存在する可換環。つまり、四則演算が定義されている集合
- 素数 p を法とした剰余環、すなわち、位数pの体を素体、あるいはガロア体と呼び、GF(p),Fpと書く
- 有限体 Falois Field, GF(q)
- 四則演算ができる有限要素の集合。qの値によって、どういう集合と計算を定義するかが異なる
- ガロア体,素体(?): qが素数の場合 GF(p) -> 素体とも呼ぶ。整数の集合 {0,1,..,p-1} を考え、mod p上での四則演算を定義することでGF(p)となる
- 原始元 g が存在し、0以外のFpの全ての要素は
g**n mod pで表現可能
- 原始元 g が存在し、0以外のFpの全ての要素は
- ガロア体(?) qが素数のべき乗の場合 GF(pn) -> 多項式の集合 {0,1, ... ,p-1,x,x+1,x+2, ... ,(p-1)xn-1+(p-1)xn-2+...+(p-1)} を考え、mod 既約多項式上での四則演算を定義することでGF(pn)となる
- 特に暗号ではGF(2n)が使われる
- 原始元 primitive element
- 体の拡大とは実数を拡大すると複素数になるように、小さい体から大きい体へパワーアップさせるような操作で、その拡大の中でも特に「正規性」と「分離性」の2性質を満たすものをガロア拡大という
- 拡大体 GF(pm)
- P(x)をGF(p)の上の多項式とする。このとき、P(x)が既約、すなわち、GF(p)上で根を有しない場合、P(x)を法とする多項式の剰余環は体を成す
- p個の元を有する体GF(p)上の多項式P(x)の次数をmとすると、pm個の元を有する体がP(x)を法とする剰余環を考えることができ、これをpm個の元を有するガロア体と呼びGF(pm)と書く
- GF(p)を基礎体と呼び、このガロア体の位数はpmであるが、このガロア体の標数はp
- 暗号ではGF(2m)が多い
- sageでは
F = GF((p, k), "x")とすると、pを法としたGF(pk)が定義できる
- sageでは
- 例えばX3+X+1を法として導かれる拡大体GF(23)は
- GF(23)={0,1,α,α+1,α2,α2+1,α2+α,α2+α+1}
- ここでαと書いているのは拡大体の元であることを強調するのにXの代わりにα,β,γを使うという慣習があるため
- 拡大体 GF(pm) で、とある元xをxpにする計算は各係数をベクトルと考えると特定の線形変換となる
- つまり、
x^p = M * xの形で書ける - これにより、xを
x^(p+1)にする変換を考えると、これは二次変換であると言える- つまり、
x = (a1, ..., am)をx' = (1, a1, ..., am, a1*a1, a1*a2, ..., am*am)と変換して考えた時(定数項も入れているが変換上はマストではない。他の線形計算も入れ込みたいなら1を入れる)にx'^(p+1) = M * x'と考えることができる
- つまり、
- 拡大体 -> ベクトル
el.polynomial().padded_list(m) - ベクトル -> 拡大体
F(list(lst))F = GF((p, m), "x")みたいな前提
- つまり、
- 何も分かっていないが、GF(p16)をGF(p2)に変換して良い感じにやるみたいな変換テクがあるっぽい
- https://blog.cykor.kr/2025/02/Write-Up-AlpacaHack
- Cutter - Codegate 2023 Finals
- Quo vadis? - ECSC 2024 Italy
- 有限体に関するテクでむっちゃむずいらしい
- pを素数としてq=pnと書くと、元の個数がqの有限体が(同型を除き)一意に存在し、Fqと書く
- このとき、体の標数はpである(1をp個足すと0になる)
- Fp⊂Fqであり、FqはFpのn次の拡大であるという
- Fqの代数閉包を‾Fqと書く
- sagemath
P.<x> = PolynomialRing(GF(2))としてF.<x> = P.quo(x^128 + x^7 + x^2 + x + 1)とするとGF(2128)が構築可能- ↑と同じ別の書き方
x = GF(2).polynomial_ring().gen()としてF = GF(2 ** 128, name="A", modulus=x^128+x^7+x^2+x+1)
- 最小多項式: 基礎体GF(p)とその拡大体が与えられたとき、拡大体の任意の元をβとする。基礎体GF(p)上で既約で m(β)=0となる最小次数のモニック多項式m(x)をβの最小多項式と呼ぶ
- 拡大体GF(23)における元αの最小多項式はh(x)=x3+x+1
数論における便利定理や分野
- 離散対数問題 -> 別稿で解説
- ガウス整数
- ガウス整数 Gaussian Integer, 実部と虚部が共に整数である複素数
- ガウス整数環 Z[i], 加法と乗法について閉じていて環であり、複素数体Cの部分環であるため整域
- https://www.tsuyama-ct.ac.jp/matsuda/eBooks/factrization_ideal.pdf が分かりやすい
- 典型テク「a2+b2=kでkが既知のときにa,bをガウス整数の素因数分解を経由して求める」
- Crypto CTF 2021 - Ferman https://blog.y011d4.com/20210801-crypto-ctf-writeup#ferman
GI = GaussianIntegers()してGI(k).factor()するとガウス整数で素因数分解できる- 素因数分解を2グループに分けて掛け合わせると(a+ib)と(a-ib)に分解でき、(a+ib)(a-ib)=a2+b2であるため、a,bが求まる
- 素因数分解すると
(I) * (-110935748565706808545452731038884*I - 1673809851384879136750725022885175)^7 * (110935748565706808545452731038884*I - 1673809851384879136750725022885175)^7 * (I - 6)^7 * (-I - 2)^7 * (2*I + 1)^7 * (I + 6)^7となり、よくよく見ると(I)以外は共役対になっているので片方だけ採用すればいい
- zer0pts CTF 2023 - easy_factoring
- Crypto CTF 2021 - Ferman https://blog.y011d4.com/20210801-crypto-ctf-writeup#ferman
- ディオファントス問題: 方程式が与えられたとき、その方程式の整数解や有理数解を求める問題
- sympy のディオファントス問題ソルバ
- ディオファントス方程式: 整数および変数の定数乗の加減乗算からなる方程式
- https://ja.wikipedia.org/wiki/%E3%83%87%E3%82%A3%E3%82%AA%E3%83%95%E3%82%A1%E3%83%B3%E3%83%88%E3%82%B9%E6%96%B9%E7%A8%8B%E5%BC%8F
- ディオファントス方程式の中でも名前が付いているものもある
- ベズー方程式
ax+by=d-> ユークリッドの互除法の応用により、一般の整数解が求まる - ピタゴラス方程式
x**2 + y**2 = z**2 - ペル方程式
x**2 - n y**2 = 1連分数の応用により、一般の整数解が求まる - 楕円曲線
y**2 = f(x)(f (x) は重根をもたない、3次または4次の多項式) - 超楕円曲線
y**2 = f(x)(f (x) は重根をもたない、5次以上の多項式) - トゥエ方程式
f(x,y)=k(f (x, y) は3次以上の斉次既約多項式)
- p進数体 p-adic
- ここが初手分かりやすい https://tsujimotter.hatenablog.com/entry/hensels-lemma
- ヘンゼルの補題
- p進対数は計算が容易なので、そのままでは離散対数が難しい体であっても同型なp進数体に変換できればp進対数として計算できるかも
- p進対数が計算できるのは、p進数の単元群(units)に属すものだけ
- つまり、正確には、とあるp進数の単元群の部分群に同型変換できれば、p進対数が使えて離散対数が解ける
log_p(g^m) = m × log_p(g)であるため
log_p(1 + x) = x - x²/2 + x³/3 - x⁴/4 + ...らしい
- p進数体
- 精度というのを指定でき、素数pに関するp進数体で、精度3であれば、a + bp + cp2という表現になる
- p進付値 |x|_p 特殊なノルム
- p進数体と同型
- (Z/pnZ)* ... Zmod(pn)上の乗法群
- 例)Zmod(p3)上でak=bのとき、kを求めたいとする
- f: x -> xp-1 という変換をすると、Z/p3Z -> Qp
- 位数がp2(p-1)から p2 に変化する(これはQpと同じ)
(Z/p^3Z)*は位数がp2(p-1)であることから、直積分解ができ、(Z/p^3Z)* ≅ (Z/(p-1)Z) × (Z/p^2Z)*であるため、p-1の累乗を取ると前半が消えて後半だけになるらしい。(ほんとに?) - 変換後の要素a'はa' = 1 mod pになる(mod p3をmod pにして(p-1)乗すると考えると分かる)ので、a' = 1 + sp であり、これはp進対数が収束する(簡単に求められる)ので、あとは対数を取って割ればkが得られる。また、p進数体の精度は mod p3 なので、3桁で取れば良い
- つまり、
FQp = Qp(p, 3)としてprint(ZZ(FQp(ct).log()/FQp(g).log()).to_bytes(43)) - p進数体の精度が自由に取れる
- Henselの持ち上げによるもので mod p で解が分かれば mod p2 や mod p3 に持ち上げられる
- = 1 mod pであれば、mod p2にしても1 + sp + tp2になるので同様にp進対数が収束する
- つまり、
- 位数がp2(p-1)から p2 に変化する(これはQpと同じ)
- (p-1)乗する写像は
exp: Z/pZ → U = 1 + p Z/pZで、p進対数はlog:U → Z/pZで、logについては(U,*) ≅ (Z/pZ, +)と加法群に変換できるから解けるみたいな話みたい
- 超特異楕円曲線 #E(Fp)=pの楕円曲線 -> Smart's Attack
- 楕円曲線は加法群なのでそのままでは適用できないが、曲線をp進数にhensel liftingすることで、形式群のp進対数が適用できる
- (Z/pnZ)* ... Zmod(pn)上の乗法群
- zer0pts ctf - pure division
- BKCTF 2023 - DH
- https://hackmd.io/@m1dm4n/bkctf2023
- 3x = A mod p11 q11を解く問題
- Crypto CTF 2022 - Starter ECC
- https://blog.maple3142.net/2022/07/18/cryptoctf-2022-writeups/
y^2=x^3+ax+b mod p^63*q^6*r^5でx,a,b,p,q,rが分かってるときにyを求める- mod p, mod q, mod rでのyの解を求めてヘンゼルリフトしてCRTする
- mod pでsqrtしなくてもmod p63のままでもp進数に変換すればそのままsqrtできる
ZZ(Qp(p, prec=63)(y2).sqrt()) % (p ^ 63)
- Infobahn CTF 2025 - YAOL
- https://medium.com/@0x4jl9/yaol-infobahn-ctf-2025-a3d8e1a279a8
5^x = y mod p^3をy,pが既知の時にxを解く
代数幾何学
- 楕円曲線 -> cry-pubkey-ec.md
- 超楕円曲線 y2=f(x)で表される代数曲線
- sagemathだと
HC = HyperellipticCurve(F, 0)で作れる - ヤコビ多様体: 超楕円曲線上の群構造。曲線上の点たちを特定の方法で加法群にしたもの
- https://mitsu1119.github.io/blog/p/%E8%B6%85%E6%A5%95%E5%86%86%E6%9B%B2%E7%B7%9A%E6%9A%97%E5%8F%B7%E3%82%92%E7%90%86%E8%A7%A3%E3%81%99%E3%82%8B%E3%81%9E/
- 問題
- SECCON 2022 Finals - Hell https://furutsuki.hatenablog.com/entry/2023/02/13/231456
- hxp CTF 2020のhyper/hyperhyper
- sagemathだと
- 3変数のディオファントス方程式を楕円曲線に変換して、楕円曲線上で有理解を求めることで元の方程式の解を得る
x/(y+z)+y/(x+z)+z/(x+y)=4を満たす自然数(x,y,z)を求める問題- https://www.quora.com/How-do-you-find-the-positive-integer-solutions-to-frac-x-y+z-+-frac-y-z+x-+-frac-z-x+y-4?share=1
- この方程式は同次であるため、実際には2次元の式である
- 同次 -> 各項の次数を計算すると0次になる。また、解(x,y,z)が存在するとき、解(nx,ny,nz)も存在し、解を全部整数倍しても値が変わらない
- 同次であれば変数間での相関関係を考えると元を1つ減らすことができる
x/(y+z)+y/(x+z)+z/(x+y)=4は変換すると、3次3元の方程式であるが、同次式であるために実質文字が1つ減り、3次2元の方程式と見ることができ、これは楕円曲線と同じである
x/(y+z)+y/(x+z)+z/(x+y)=4を楕円曲線に変換するにはEllipticCurve_from_cubic(eq.numerator(), [1, 0, 0])なんと一本で変換できる関数がある https://github.com/hackthebox/uni-ctf-2023/tree/main/uni-ctf-2023/crypto/%5BHard%5D%20Zombie%20Rolled
- https://shironetsu.hatenadiary.com/entry/2021/05/26/003510
- 丁寧記事。
- https://mlzeng.com/an-interesting-equation.html
- https://triodelzx.github.io/2025/08/13/%E4%B8%80%E9%81%93%E7%BB%8F%E5%85%B8%E6%95%B0%E8%AE%BA%E9%97%AE%E9%A2%98%E7%9A%84%E8%A7%A3%E7%AD%94%E4%B8%8E%E7%AE%80%E8%A6%81%E5%88%86%E6%9E%90/
関数 / 多項式
- 関数、写像 f: y=f(x)のように書き、集合Xの元xを集合Yの元yに対応させる規則を指す
- 高次方程式の求解
- 二次方程式を解く
from sage.all import PolynomialRing- 変数作る
R.<x> = PolynomialRing(ZZ) - 連立方程式を作る(=0にして)
f = x*(x*r1+(x+i)*r)-n - 解く
res = f.roots() - 解を得る
len(res) > 0なら解けていて、q = res[0][0]みたいにとって来る
- 変数作る
- 連立方程式を解く
from sage.all import PolynomialRing- 変数作る
R.<p, q> = PolynomialRing(ZZ) - 連立方程式を作る(=0にして)
f1 = N - p * qf2 = q * (r + b) + (q + a) * r - p - 解く
rr = f1.resultant(f2).univariate_polynomial().roots() - 解を得る
len(rr) > 0なら解けていて、q = rr[0][0]みたいにとって来る
- 変数作る
- 合同方程式を解く
- 法が素数の場合は若干無理しても多分解ける
- 複数答えが出てくるので全部試す。↑のものは8次式(多分)だが一瞬で正しい答えが出てきた
- 法が2の累乗の場合
- とりあえず2次合同方程式は直接は解けなかった。
R.<p> = PolynomialRing(GF(m)); f = ap * p^2 + cp * p - n; roots = f.roots()がダメだった- この場合は、良い感じに分解してやれば解けた
- ax2+bx+c=0を(x+b/(2a))2=b2/(4a2)-c/aと変換して、
mod(右辺, m).sqrt(all=True)のようにmodのsqrtを取ってx+b/(2a)の値を得る - x+b/(2a)=(1の解)の1次合同方程式を解く
solve_mod(2 * x == int(((-1 * cp * pow(ap, -1, m)) + root) % m), m)
- ax2+bx+c=0を(x+b/(2a))2=b2/(4a2)-c/aと変換して、
- この場合は、良い感じに分解してやれば解けた
- とりあえず2次合同方程式は直接は解けなかった。
- 法が合成数の時
- 1次なら解けるが、2次以降は素因数分解できないと厳しい
- 合同方程式をCRTする
- https://blog.y011d4.com/20210829-cakectf-writeup#party-ticket
- osu!gaming CTF 2025 - please-nominate
- 複数の法の異なる方程式をCRTで合成してmodの法を大きくして、coppersmithする
- 自分はf1(x) mod n1, f2(x) mod n2, f3(x) mod n3をn2n3f1(x) + n1n3f2(x) + n1n2f3(x) mod n1n2n3にしてcoppersmithしたけど、CRTでうまく合成している人もいるっぽい
- 複数の法の異なる方程式をCRTで合成してmodの法を大きくして、coppersmithする
- 不動点 f(x) = x を満たす xのこと
- irreducible polynomial -> 既約多項式
- 128次多項式は簡単に因数分解可能
- sagemathのfactorで可能
python (sagemath) l = list(PQ.factor()) P = l[0][0] Q = l[1][0]
- sagemathのfactorで可能
- https://github.com/tsg-ut/tsgctf2023/blob/main/crypto/complicated_function/writeup.ja.md
q = f(p) = isqrt(p^2 + (2^512 - 6)p + cail(sqrt(p * sin p))) + 2^1023とpがあってN=pqのNが与えられている- 二分探索:f(p)はpに関して単調増加している(「p*sin p」部分は振動しているが影響が小さい)ので、pを増やすとN=pqも単調増加するため、pを二分探索することでN=pqとなるpが求められる
- f(p)-pの収束:f(p)の係数項やsqrt(p*sin p)部分はpに対して大分小さく、pを大きくしていくにつれて影響が小さくなる。つまり、支配的になるのはisqrtの中のp2部分だけとなり、f(p)はpを大きくしていくと、p+定数に近づいていくと想像できる。pが1024bitsくらいだと整数部分では既に収束していて、実際21023+2511-4であることが分かるため、q=p+Cと書くことができ、N=pq=p(p+C)となりpに関する二次方程式となるためpが求まる
- 用語
- 解: 方程式の答え
- 根: 多項式に対して=0としたときの答え
- 終結式 https://ccmath.meijo-u.ac.jp/~mumr/2015/pdf/2015_04.pdf
- 消去式 resultant
- 多項式をもっと早く解く方法
- idekCTF 2025 - Diamond Ticket https://giapppp.github.io/posts/idekctf-2025/
s**73331 + s - fc = 0 mod pを解くと時間が無茶苦茶かかるので、mod p上であることを生かし、s**p - s = 0 mod pが成立することから、gcd(s**73331 + s - fc, s**p - s)をした結果の根を取ることでs mod pを求める。格段に速くなる
- idekCTF 2025 - Diamond Ticket https://giapppp.github.io/posts/idekctf-2025/
- 9次式くらいならさっと解いてくれそう https://blog.y011d4.com/20210919-acsc-writeup#swap-on-curve
- f(x)=a0+a1x+a2x2+...+anxnのような関数があり、任意の入力mに対してf(m)の結果が得られるとき、適当に小さい素数pを使ってf(p)を取得し、pの余りを取ってa0、pで割ってpの余りを取ってa1、...みたいにして係数を特定できる
- https://7rocky.github.io/en/ctf/other/crewctf/po1337nomial/ これを頑張って誤差はバックトラックで補間していく
- ラグランジュ補間
- n次の関数 y = a_0 + a_1 x + ... a_n xn の上の点がn+1分かっていれば関数が復元できる
- 剰余環上でも使えるが、法は素数である必要がある(ほんとに?)
- 多項式展開するとき
g(X,Y,Z).simplify_full().expand()
グレブナー基底(グラブナー)
- 計算量 最悪ケース: 二重指数時間 (doubly exponential)
- 変数の数 n、次数 d に対して O(d2n) またはそれ以上
- 例えば、変数: 35個、次数: 3 (各要素が3乗)、方程式: 35個ならばd2n = 3235 ≈ 334億 という天文学的な計算量
- 最悪では O(22n)
- 変数の数 n、次数 d に対して O(d2n) またはそれ以上
- グレブナー基底を使うモチベーション:「多項式の集合を、計算しやすい形に整理するための手法」
- 複数の多項式の集合を与えることで、より計算しやすい、簡単な、かつ、解が等しくなる方程式が得られる
- 利用例
c1 = pow(m + s, e, n)とc2 = pow(m + s * t1, e, n)とc3 = pow(m * t2 + s, e, n)で、t1,t2,eが既知で、m,sが変数 ->m^17+Aが得られた
- zer0pts CTF 2021 - NOT Mordell Primes
- 数学的には(ChatGPTに聞いたから微妙かも)
- グレブナー基底を計算するアルゴリズム
- グレブナー基底が計算できる前提条件
- 簡約グレブナー基底というのもある
- [質問] 変数除去をするだけなら終結式の方が確実な気がするんですけど、終結式に比べたグレブナー基底のメリットみたいのってありますか?
- 確かにグレブナー基底使った方が計算早く終わったケースもあったので次数で使い所決めてみます
- 変数除去には他にも色々やり方があるらしい
- 勉強するとき
- Ideals, Varieties, and Algorithms https://link.springer.com/book/10.1007/978-3-319-16721-3 by y011d4さん
- イデアルとか、ヒルベルト基底定理とか
- シンボリック実行して、グレブナー基底
- 「終結式はシルベスター行列の行列式と等しい。」 https://manabitimes.jp/math/1202
他
- neary equalをうまく使い、式を単純化することで、求めたい変数の近似値を求め、そこから周辺を全探索して条件に合うものを探してくる
- AHR9 RSAMPC
- bit数が倍くらい違えば小さいbitは無視して計算を進めると出てきた結果が誤差±10くらいになったりする
- なんでこんなに近い値が出てくるのかはよく分かってない。512bitsくらいのものを無視したら、512bitsくらいのズレか、二次関数の一部であればsqrtである256bitsくらいのズレになるならなんとなく分かるが、そんなに近い値が出てくるの?
- 近似の式はそのままで導出できなかったりするので、二分探索とかなんかで頑張って求める
- bit数が倍くらい違えば小さいbitは無視して計算を進めると出てきた結果が誤差±10くらいになったりする
- SECCON Beginner CTF 2025 - mathmyth https://zenn.dev/claustra01/articles/873cac6a6ad323#%5Bcrypto%2C-hard%5D-mathmyth
- 1064bit + 1016bitで後半を無視して近似してうまいことやってた
- Nullcon Goa HackIM 2025 CTF - next-level
p = number.getPrime(512)でq = nextprime(p)でr = nextprime(q)であるとき、p,q,rはとても近い値になるので、N=pqrの3乗根を取って近辺を探すと大体p,q,rになっている
- Cyber Apocalypse CTF 2021 - Wii Phit
- https://blog.y011d4.com/20210424-cyber-apocalypse-ctf-writeup#wii-phit
- 全体のサイズ感にもよりそうだけど、1000離れていると結構違うと判断している
w(xz+yz-wy)=4xyzだとxyzで割りたくなって、全体と比較して、1/zは比較的小さいので無視して、1/(x+1)は1/xに近似していい感じにやるとうまくいく
- SECCON CTF 2021 - CCC https://project-euphoria.dev/blog/29-seccon-2021/#ccc
an = (ap+b)^3 - b^3をan ≈ (ap+b)^3と近似して、三乗根を取ってその周辺を探索して解く
- AHR9 RSAMPC
- 近似をうまく使う https://yun.ng/c/ctf/2024-0xl4ugh-ctf/crypto/18-karat-gold
- 実験すると、q≈m_qであるときqをm_qに置き換えて逆計算をするとうまくいったりする(誤差ゼロ!)
- n=(rp-p4)(rq-q4)で、rp,rqが24bitsでp,qが256bitsだと大小差から、n≈p4q4としてしまい、pq=n1/4で完全にpqが求まった
int(n ** (1 / 4))https://blog.y011d4.com/20220629-actf-writeup#secure-connection