昨日参加した Hack.lu CTF 2020 で解けなかった暗号問題 Bad Primes の復習となります。CTFtime に載っている writeup を参考にしました。この writeup を見ると Stack Exchange を引用しています。
問題設定
基本的には普通の RSA と一緒です。ただし公開鍵 を計算するための素数 は既知とします。また、 です。 このとき、ある平文 を暗号化した が与えられており、それらを使って を復号するという問題です。
従来の RSA と異なるのは、本来ですと となるような素数 を用いるのですが、この問題では となっており、 です。すると となる が存在せず、 RSA での復号方法 は成立しません。これを復号するにはどうすればいいでしょうか。
実は次のことが成立しているとき、 の候補を 個に絞ることができます。
- は素数
- かつ
復号方法
- ( は位数が となる任意の整数)
としたとき、 () が の候補となります。
証明
まず、 で計算される が を満たすことを示します。 証明には カーマイケルの定理 を使います。カーマイケルの定理より、 となる を用いて、 () となります。この が「復号方法」で定義した となります。 は法を としたときの の逆元という定義ですが、これは が でちょうど1回しか割り切れないことから存在が示せます。 実際に で求まる値を として計算すると、
となります。これを 乗すると、 となるため、このようにして求まる は を満たします。
次に のとき であることと、 を示します。 これ の命題9.11 (1) を使います。再掲すると、 を2以上の自然数、 を と互いに素な整数、 を法 に関する の位数としたとき、 ならば法 に関する の位数は となります。 に上記命題を適用すると、 の位数は となります。位数の定義により、示せました。
以上をもって について をそれぞれ計算すれば、 通りの解が求まります。今 となる はたかだか個しかないため、この方法で計算される のどれかが となります。
実装
Hack.lu CTF 2020 の Bad Primes について解いてみました。
from Crypto.Util.number import long_to_bytes, inverse, GCD
n = 3283820208958447696987943374117448908009765357285654693385347327161990683145362435055078968569512096812028089118865534433123727617331619214412173257331161
p = 34387544593670505224894952205499074005031928791959611454481093888481277920639
q = 95494466027181231798633086231116363926111790946014452380632032637864163116199
e = 65537
c = 2152534604028570372634288477962037445130495144236447333908131330331177601915631781056255815304219841064038378099612028528380520661613873180982330559507116
assert p * q == n
_lambda = (p - 1) * (q - 1) // GCD(p - 1, q - 1)
assert _lambda % e == 0
assert _lambda // e % e != 0
L = pow(2, _lambda // e, n)
assert L > 1
d = inverse(e, _lambda // e)
assert e * d % (_lambda // e) == 1
for i in range(e):
tmp_flag = long_to_bytes(pow(c, d, n) * pow(L, i, n) % n)
if b"flag" in tmp_flag:
print(tmp_flag)
flag{thanks_so_much_for_helping_me_with_these_primitive_primes}