|
| 1 | +# Easy RSA |
| 2 | + |
| 3 | +**公式不渲染警告**,建议移步至[我的博客](https://blog.monadx.com/2021/10/31/Hackergame-2021/#easy-rsa)。 |
| 4 | + |
| 5 | +观察代码,我们有三个任务: |
| 6 | + |
| 7 | +1. 计算 `p` |
| 8 | +2. 通过 `value[-1]` 反推全部 `value` |
| 9 | +3. 通过 `value_q` 反推 `q` |
| 10 | + |
| 11 | +## 1. 计算 `p` |
| 12 | + |
| 13 | +查找资料,可以找到一个叫“威尔逊定理”的东西,即当 `p` 为质数的时候,有: |
| 14 | + |
| 15 | +$$ |
| 16 | +(p - 2)! \equiv 1 \enspace (\operatorname{mod} p) |
| 17 | +$$ |
| 18 | + |
| 19 | +则: |
| 20 | + |
| 21 | +$$ |
| 22 | +y! \equiv (x - 2)! \times \frac{y!}{(x - 2)!} \equiv \frac{1}{\prod_{i = y + 1}^{x - 2} i} \equiv \prod_{i = y + 1}^{x - 2} inv(i) \quad (\operatorname{mod} x) |
| 23 | +$$ |
| 24 | + |
| 25 | +代码: |
| 26 | + |
| 27 | +```python |
| 28 | +def get_p(x, y): |
| 29 | + mul = 1 |
| 30 | + for i in range(y + 1, x - 2 + 1): |
| 31 | + mul = mul * pow(i, x, x - 2) % x |
| 32 | + return mul |
| 33 | +``` |
| 34 | + |
| 35 | +## 2. 反推 `value` |
| 36 | + |
| 37 | +这个部分涉及到一个最主要的函数就是 `sympy.nextprime`,先看看它的文档: |
| 38 | + |
| 39 | +> `def nextprime(n, ith = 1)` |
| 40 | +> Return the ith prime greater than n. |
| 41 | +> See Also: prevprime: Return the largest prime smaller than n. |
| 42 | +
|
| 43 | +……我感觉官方文档都告诉你应该怎么做了。 |
| 44 | + |
| 45 | +```python |
| 46 | +def get_first_value(last_value): |
| 47 | + val = last_value |
| 48 | + for _ in range(9): |
| 49 | + val = sympy.prevprime(val) |
| 50 | + return val |
| 51 | +``` |
| 52 | + |
| 53 | +## 3. 反推原始 `q` |
| 54 | + |
| 55 | +这个部分就是给出这个式子(我们把这个原始的 `q` 称为 `x`): |
| 56 | + |
| 57 | +$$ |
| 58 | +q \equiv x^e \enspace (\operatorname{mod} n) |
| 59 | +$$ |
| 60 | + |
| 61 | +已知 `e`, `n`, `q`,求 `x`。 |
| 62 | + |
| 63 | +然而我不会求,查资料,发现了这个:[Finding the k-th root modulo m](https://math.stackexchange.com/questions/2073284/finding-the-kth-root-modulo-m),简直完美。 |
| 64 | + |
| 65 | +按照这篇文章(回答)所说,首先求满足这一式子的 $k$, $v$: |
| 66 | + |
| 67 | +$$ |
| 68 | +u \, e - v \, \varphi(n) = 1 |
| 69 | +$$ |
| 70 | + |
| 71 | +显然 $\gcd(e, \, \varphi(n)) = 1$,这一步可以用 exgcd 做。 |
| 72 | + |
| 73 | +求出来了之后,就有 |
| 74 | + |
| 75 | +$$ |
| 76 | +x \equiv (b^u)^e \enspace (\operatorname{mod} n) |
| 77 | +$$ |
| 78 | + |
| 79 | +写成代码,就是: |
| 80 | + |
| 81 | +```python |
| 82 | +# 这里的 n 我们通过 `first_value` 生成 |
| 83 | +def get_original_q(e, q, first_value): |
| 84 | + value = [ first_value ] |
| 85 | + n = first_value |
| 86 | + for i in range(1, 10): |
| 87 | + value.append(sympy.nextprime(value[i - 1])) |
| 88 | + n *= value[-1] |
| 89 | + |
| 90 | + # 这样求 phi 可以快不少,该函数要求 factors 中的 keys 都是质数。 |
| 91 | + factors = { v: 1 for v in value } |
| 92 | + pn = sympy.ntheory.factor_.totient._from_factors(factors) |
| 93 | + |
| 94 | + # Ex-GCD, 其中 h = gcd(e, pn);u, v 含义如上所述。 |
| 95 | + (u, v, h) = sympy.polys.polytools.gcdex(e, pn) |
| 96 | + |
| 97 | + x = pow(q, int(u) * e, n) |
| 98 | + return x |
| 99 | +``` |
| 100 | + |
| 101 | +经过验证,以上三个脚本都可以算出的结果,代入进源程序中,都可以算出源码中对应的结果。 |
| 102 | + |
| 103 | +## 4. RSA 解密 |
| 104 | + |
| 105 | +RSA 解密还需要一个数 $d$(含义请自行翻看 RSA 文档,也可以看[这篇](https://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html))。 |
| 106 | + |
| 107 | +算 $d$ 满足以下式子: |
| 108 | + |
| 109 | +$$ |
| 110 | +ed \equiv 1 \enspace (\operatorname{mod} n) |
| 111 | +$$ |
| 112 | + |
| 113 | +在先进的 `Python 3.8+` 中,可以直接用 `pow(x, -1, mod)` 来算逆元,省事不少。 |
| 114 | + |
| 115 | +于是解密部分的代码: |
| 116 | + |
| 117 | +```python |
| 118 | +pn = sympy.ntheory.factor_.totient._from_factors({ p: 1, q: 1 }) # phi(n) |
| 119 | + |
| 120 | +d = pow(e, -1, pn) |
| 121 | + |
| 122 | +c = 110644875422336073... |
| 123 | +m = pow(c, d, p * q) |
| 124 | +print(m) |
| 125 | +print(m.to_bytes(32, "big")) |
| 126 | +``` |
| 127 | + |
| 128 | +然后就可以拿到 flag 了。 |
0 commit comments