# RSA
> Author:堇姬
# 代號
| 符號 | 意思 |
| --- | ----|
|m|明文 |
|c|密文|
|e|公鑰|
|d|私鑰|
|n|模數|
|p & q|n分解的因數|
|$ϕ(n)$|(p-1)x(q-1)|
# 加密
1. 找到兩個質數$p$ , $q$使得$n=p*q$,需要滿足:
$n>m$ 且 $2q>p>q$
2. 找到一個公鑰e,滿足$gcd(e,ϕ(n))=1$
3. $c=m^e \pmod n$
加密python
```python=
from Crypto.Util.number import *
import gmpy2
import codecs
msg = ""
hex_msg = int(codecs.encode(msg.encode(), 'hex'), 16)
p=getPrime(100)
q=getPrime(100)
n=p*q
e=65537
phi=(p-1)*(q-1)
d=gmpy2.invert(e,phi)
print("phi=",phi)
print("d=",d)
c=pow(hex_msg,e,n)
print("p=",p)
print("q=",q)
print("n=",n)
print("e=",e)
print("c=",c)
```
# 解密
1. 分解$n$,得到$p$ , $q$
2. $ϕ(n)=(p-1)× (q-1)$
3. 找出d,$ed \equiv 1 \pmod {ϕ(n)}$
4. $m=c^d \pmod n$
### 解密script
```python=
from Crypto.Util.number import *
import gmpy2
p=
q=
e = 65537
c =
n=p*q
phi = (q - 1) * (p - 1)
d = gmpy2.invert(e , phi)
print(long_to_bytes(pow(c , d , n)))
```
# 證明解密
>證明 $m=c^d \pmod n$
目前已知$c=m^e \pmod n$
$m \equiv c^d \pmod n \equiv m^{ed} \pmod n \equiv m^{ϕ(n)×k+1} \pmod n$
(因為$ed \equiv 1 \pmod {ϕ(n)}$ 故 $ed=ϕ(n)×k+1$)
故現在只要證明$m \equiv m^{ϕ(n)×k+1} \pmod n$
>證明$m \equiv m^{ϕ(n)×k+1} \pmod n$
分兩種情況討論$m$ , $n$是否互質?
>$m$ , $n$互質
歐拉定理可知
$m^{ϕ(n)} \equiv 1 \pmod {n}$
$m^{kϕ(n)} \equiv 1^k \pmod {n}$
$m^{kϕ(n)+1} \equiv m \pmod {n}$
得證
>$m$ , $n$不互質
因為$m<n$,故$m$只能是$p$的倍數,或是$q$的倍數。(如果m是pq倍數則 $m<n$ 不成立)
假設$m$為$p$之倍數,$m=wp$ , $w \in \mathbb{N}$ ,且$gcd(m,q)=1$
費馬小定理可知
$m^{ϕ(q)} \equiv 1 \pmod {q}$
$m^{k×ϕ(q)} \equiv 1^k \pmod {q}$
$m^{k×ϕ(q)×ϕ(p)} \equiv 1^{k×ϕ(p)} \pmod {q}$
$m^{k×ϕ(n)} \equiv 1 \pmod {q}$
$m^{k×ϕ(n)} \equiv 1+rq \pmod {q}$,$r\in \mathbb{Z}$
兩邊同乘$m$
$m^{k×ϕ(n)+1} = m+rqm$
$m=wp$代入
$m^{k×ϕ(n)+1}=m+rqwp$
$m^{k×ϕ(n)+1}=m+rwn$
存在$\pmod n$時,$m^{ϕ(n)×k+1} \equiv m \pmod n$,得證
>故m,n不管有無互直皆成立,故$m=c^d \pmod n$正確
# 一些常見RSA題目
## n分解
[factordb](http://www.factordb.com/)
## e過小,n過大
$c=m^e \pmod n$
若$m^e <n$
則是否$\pmod n$沒差
$c=m^e$
將c開e次方根即可得到m
```py
from gmpy2 import *
from Crypto.Util.number import *
n=
e= 5
c=
flag = iroot(c, e)[0]
flag_bytes = long_to_bytes(flag)
print(flag_bytes)
```
## p、q相近(p、q為相鄰質數)
$\sqrt{(p×q)}$一定介於p、q間,故我可以用`gmpy2.next_prime()`
```python=
from Crypto.Util.number import *
import gmpy2
p = getPrime(512)
q = gmpy2.next_prime(p)
n=p*q
print(p)
print(q)
print(n)
temp=gmpy2.iroot(n,2)[0]
p=gmpy2.next_prime(temp)
q=n//p
print(p)
print(q)
```
## Common-Factor
如果$n_1,n_2$有一公因數$p$
$n_1=p \times q_1$
$n_2=p \times q_2$
那直接$gcd(n_1,n_2)$就可以找出$p$
```python=
from math import gcd
from Crypto.Util.number import *
n1 =
n2 =
c =
n = n1
p = gcd(n1, n2)
q = n // p
e = 65537
d = inverse(e, (p - 1) * (q - 1))
m = pow(c, d, n)
print(long_to_bytes(m))
```
## Common-Modulus attack
### 條件
$C_1 \equiv M^{e_1} \pmod N$
$C_2 \equiv M^{e_2} \pmod N$
### 證明
$gcd(e_1,e_2)=1$
貝祖等式可知$e_1 \times t+e_2 \times u=1$
所以如果我把
$C_1^{t} \times C_2^{u} \pmod n$可以求出$m$
因為$C_1^{t} \times C_2^{u} \pmod n=M^{e_1t}\times M^{e_2u}\pmod n =m^1\pmod n$
所以可以直接藉由算出$t,u$來求$m$
### code
```python=
from gmpy2 import gcdext
from Crypto.Util.number import *
n =
c1 =
c2 =
e1 =
e2 =
_, t, u = gcdext(e1, e2)
m = pow(c1, t, n) * pow(c2, u, n) % n
print(long_to_bytes(m))
```
## Wiener attack
https://zhuanlan.zhihu.com/p/400818185
```python=
#!/usr/bin/env python3
import gmpy2
from Crypto.Util.number import *
n =
e =
c =
def cf_expansion(n, d):
q, r = n // d, n % d
e = [q]
while r != 0:
n, d = d, r
q, r = n // d, n % d
e.append(q)
return e
def convergents(e):
n, d = [], []
for i in range(len(e)):
if i == 0:
ni, di = e[i], 1
elif i == 1:
ni, di = e[i] * e[i-1] + 1, e[i]
else:
ni, di = e[i] * n[i-1] + n[i-2], e[i] * d[i-1] + d[i-2]
n.append(ni)
d.append(di)
yield (ni, di)
def solve(a, b, c):
k = b * b - 4 * a * c
if k < 0:
return []
sk, complete = gmpy2.iroot(k, 2)
if not complete:
return []
return [int((-b + sk) // (2 * a)), int((-b - sk) // (2 * a))]
def wiener(n, e):
kd = convergents(cf_expansion(e, n))
for i, (k, d) in enumerate(kd):
if k == 0:
continue
phi = (e * d - 1) // k
roots = solve(1, phi - n - 1, n)
if len(roots) == 2:
p, q = roots
if p * q == n:
return (p, q)
p, q = wiener(n, e)
d = inverse(e, (p - 1) * (q - 1))
m = pow(c, d, n)
print(long_to_bytes(m))
```
## Broadcast attack
已知
$C_0=m^e \pmod {N_0}$
$C_1=m^e \pmod {N_1}$
$C_2=m^e \pmod {N_2}$
可以用中國剩餘定理直接求出$M$
$N=N_0 \times N_1 \times N_2$
$K_i= \frac{N}{N_i}$
$m \equiv (C_0K_0K_0^{-1}+C_1K_1K_1^{-1}+C_2K_2K_2^{-1}) \pmod N$
```python=
import gmpy2
import functools
from Crypto.Util.number import *
n0 =
c0 =
n1 =
c1 =
n2 =
c2 =
def crt(a, m):
'''
Input: [a_1, ... a_n], [m_1, ..., m_n]
x = a_1 (mod m_1)
x = a_2 (mod m_2)
...
x = a_n (mod m_n)
Output: x
'''
prod, total = functools.reduce(lambda x, y: x * y, m), 0
for ai, mi in zip(a, m):
Mi = prod // mi
total += ai * Mi * (gmpy2.gcdext(Mi, mi)[1] % mi)
return total % prod
c = crt([c0, c1, c2], [n0, n1, n2])
m, _ = gmpy2.iroot(c, 3)
print(long_to_bytes(m))
```
## LSB Oracle Attack
### 使用場景
有$n,c,e$,給你decrypt,你可以自由決定$c$,但你只會之到最低位(LSB)
```python=
c = int(input())
m = pow(c, d, n)
print(f'm & 1 = {m & 1}')
```
### 原理
已知$m^e \equiv c \pmod n$
先構造
$c \times 2^e$
然後傳入Decrypt可以得到$2m$的LSB
可知
$c \times 2^e \equiv m^e \times 2^e \equiv (2m)^e \pmod n$
> 如果LSB=0,代表 $0< m < \frac{n}{2}$
因為$0< m < \frac{n}{2}$
所以可以知道$2m<n$
$2m \pmod n 取LSB=0$ 因為$2m \pmod n$是偶數
> 如果LSB=1,代表 $\frac{n}{2} < m < n$
因為$\frac{n}{2} < m < n$
所以可以知道$n< 2m < 2n$
$2m \pmod n \equiv 2m-n \pmod n$
$2m-n \pmod n$是奇數(因為$n$是奇數)
所以可得$LSB=1$
這樣就可以利用這種特性來逼近$m$(像是二分搜)
進一步推廣:
構造
$c \times 2^{ke}$
丟入decrypt可以知道,$m \times 2^k$的LSB
因為 $c \times 2^{ke} \equiv 2^{ke} \times m^e \equiv (m \times 2^k)^e \pmod n$
> $\frac{tn}{2^k}<m<\frac{(t+1)n}{2^k}$ 若$LSB=0$, $t$是偶數
$tn<m\times 2^k<(t+1)n$
$2^k \times m \pmod n$的LSB=0
因為
$(2^k \times m)-tn \pmod n$
$tn$是偶數
> $\frac{tn}{2^k}<m<\frac{(t+1)n}{2^k}$ 若$LSB=0$, $t$是奇數
$tn<m\times 2^k<(t+1)n$
$2^k \times m \pmod n$的LSB=1
因為
$(2^k \times m)-tn \pmod n$
$tn$是奇數
## coppersmith
### Known-High-Bits-of-p
```sage=
from Crypto.Util.number import *
p = 0xf57c2c41811e6ef2ca843ffb3359977492f008736e5a3bf6c68e0337b5b93068a74ae01ece215c3c600000000000000000000000000000000000000000000000
n = 113954847378441551522493500984968465847633018880980771590278625504187861167681900872012944653475160211701661736300260194529508005923878166463824278227650138659888808556964271203295384802830741010509561888483441625833400734813257304376142148262455145898253949196509180159943843746225418174830196930437005987137
c = 110960995509593753586414445058090056192022711249034261327970072972237842358984979109482036607371525366794507213368401938143980296502371403082253168041199129918895635161476566764483118093417793289228317962233576518323809551513969693053116919429488737916057885903771772385273439771270145440651596683373514111754
P.<x> = PolynomialRing(Zmod(n))
f = p + x
roots = f.small_roots(beta = 0.5)
if roots:
p = int(p + roots[0])
q = n // p
e = 65537
d = inverse(e, (p - 1) * (q - 1))
m = pow(c, d, n)
print(long_to_bytes(m))
```
## 資源
https://thescipub.com/pdf/jcssp.2006.665.671.pdf
## Pollard's p-1 Algorithm
### B-powersmooth
對於質數$p$,$(p-1)$的質因數$d^k$為$B$,此質數稱為$B-powersmooth$
ex:$1303, 1302=2 \times 3 \times 7 \times 31$那麼質數$1303$就是$31-powersmooth$
### 條件
$(p-1)$的$B$很小,$(p-1)$可以整除$1 \times 2 \times ...\times B$
### 證明
知道$p|n$
建立一個$d=a^{p-1}-1$,可以得到$p|d$
$a^{p-1} \equiv 1 \pmod p$
$d \equiv a^{p-1}-1 \equiv 0 \pmod p$
如果能找到正確的$d$,就可以$gcd(a^{p-1}-1 ,n)=p$
通常$a$從2開始
我們知道$(p-1)|B!$
$2^{B!}=2^{k(p-1)}=1 \pmod p(gcd(2^k,p)=1)$
$gcd(2^{B!}-1,n)=p$
### 流程舉例
```
Input : 1403
Output : Prime factors of 1403 are 61 23.
Explanation : n = 1403, a = 2, i = 2
1st Iteration:
a = (2^2) mod 1403 = 4
d = GCD(3, 1403) = 1
i = 2 + 1 = 3
2nd Iteration:
a = (4^3) mod 1403 = 64
d = GCD(63, 1403) = 1
i = 3 + 1 = 4
3rd Iteration:
a = (64^4) mod 1403 = 142
d = GCD(141, 1403) = 1
i = 4 + 1 = 5
4th Iteration:
a = (142^5) mod 1403 = 794
d = GCD(793, 1403) = 61
Since 1 < d < n, one factor is 61.
d' = 1403 / 61 = 23.
```
### code
```python=
from math import *
def pollard_algorithm(n: int):
a = 2
b = 2
while True:
a = int(a**b%n)
p = int(gcd(a - 1, n))
if 1 < p < n:
return p, n // p
b += 1
```
### 特殊題目-1
給你decrypt但不給你直接decrypt密文
```python=
from Crypto.Util.number import bytes_to_long ,long_to_bytes
e=65537
enc_password=2575135950983117315234568522857995277662113128076071837763492069763989760018604733813265929772245292223046288098298720343542517375538185662305577375746934
n=11015196904712844451510388041761752905176927086891990452575094958019132303573528523602736380438085957767669618870291908056743033313505287486867034650555942
print(pow(2,e,n)*enc_password)
# (2^e*enc_password)^d %n =password
password=int("6468c4c6c4",16)
password=password//2
print(long_to_bytes(password))
```