# 密碼學數論 / 筆記 ###### tags: `資安` [TOC] ## 費馬小定理 a^(p-1) = 1 (mod p) p is prime $\implies$ $a^{(p-1)} \equiv 1\ (mod\ p)$ 延伸: ### a^p = a (mod p) $$ \begin{aligned} a^p &\equiv a^{p-1} \times a &(mod\ p) \\ &\equiv 1 \times a &(mod\ p) &\leftarrow 費馬小定理 \\ &\equiv a &(mod\ p) \\ \end{aligned} $$ ### a^(p-2) = a^(-1) (mod p) $$ \begin{aligned} a^{p-2} &\equiv a^{p-1} \times a^{-1} &(mod\ p) \\ &\equiv 1 \times a^{-1} &(mod\ p) &\leftarrow 費馬小定理 \\ &\equiv a^{-1} &(mod\ p) \\ \end{aligned} $$ ### (a+b)^p = a^p + b^p (mod p) $$ \begin{aligned} (a+b)^p &\equiv a+b &(mod\ p) &\leftarrow 費馬小定理延伸一 \\ &\equiv a^p + b^p &(mod\ p) &\leftarrow 費馬小定理延伸一 \end{aligned} $$ ## 尤拉定理 $a^{\phi(n)} \equiv 1\ (mod\ n)$ $\phi(n)$: 在 $\leq$ n 的數中與 n 互質 ($\bot$) 的數 ### phi(n) 1. p is prime: $\phi(p) = p - 1$ 2. $\phi(p^k) = p^{k-1} \times (p-1)$ 3. m, n 互質: $\phi(m \times n) = \phi(m) \times \phi(n)$ ## 二次殘差 Quadratic Residues 定義: 如存在一數 a 使 $a^2 \equiv n\ (mod\ p)$,則 n 為二次殘差 QR,否則為非二次殘差 QNR 性質: ``` QR * QR = QR QR * QNR = QNR QNR * QNR = QR QR ^ e = QR ``` 如何判斷一數是否為 QR: ### 勒讓德符號 Legendre Symbol 當 p 為奇數質數 $$ (\frac{n}{p}) \equiv n^{(p-1)/2}\ (mod\ p) = \left\{ \begin{aligned} +1 &, \ n\ 為\ QR \\ -1 &, \ n\ 為\ QNR \\ 0 &, \ n \equiv 0\ (mod\ p) \\ \end{aligned} \right. $$ 尋找 a 的方式: ### p = 3 (mod 4) $a = \pm n^{(p+1)/4}$ proof: $$ \begin{aligned} (\pm n^{(p+1)/4})^2 & \equiv a^{(p+1)/2} &(mod\ p) \\ & \equiv a^{(p-1+2)/2} &(mod\ p) \\ & \equiv a^{(p-1)/2+1} &(mod\ p) \\ & \equiv a^{(p-1)/2} \times a &(mod\ p) \\ & \equiv 1 \times a &(mod\ p) &\leftarrow 勒讓德符號 \\ & \equiv a &(mod\ p) \end{aligned} $$ ### Tonelli-Shanks algorithm (p = 1 (mod 4)) [wikipedia: Tonelli–Shanks algorithm](https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm) 當 p 為質數 ```python= def tonelli_shanks(y2:int, p:int) -> int: assert pow(y2, (p-1)//2, p) == 1, "Not quadratic residues" assert p%2 == 1, 'p is not prime' if(p%4 == 3): y = pow(y2, (p+1)//4, p) return y # 1 S = 0 Q = p-1 while(Q % 2 == 0): Q //= 2 S += 1 assert Q*(2**S) == p-1 # 2 z = 2 while(pow(z, (p-1)//2, p) != p-1): z += 1 assert pow(z, (p-1)//2, p) == p-1 # 3 M = S c = pow(z, Q, p) t = pow(y2, Q, p) R = pow(y2, (Q+1)//2, p) # 4 while(True): if(t == 0): return 0 elif(t == 1): return R else: i = 1 t2 = pow(t, 2, p) for i in range(1, M): if((t2-1) % p == 0): break t2 = pow(t2, 2, p) b = pow(c, pow(2, M-i-1), p) M = i c = pow(b, 2, p) t = (t * c) % p R = (R * b) % p if __name__ == "__main__": y2 = 5 p = 41 y = tonelli_shanks(y2, p) assert pow(y,2,p) == y2 assert pow(-y,2,p) == y2 ``` ## 中國餘式定理 CRT [wikipedia: 中國剩餘定理](https://zh.wikipedia.org/zh-tw/中国剩余定理) ```python= import numpy as np ai = [2, 3, 5] ni = [5, 11, 17] N = np.prod(ni, dtype=np.int) Mi = [int(N//x) for x in ni] ti = [pow(m,-1,n) for m, n in zip(Mi, ni)] ans = sum([a*t*m for a, t, m in zip(ai, ti, Mi)]) print(ans % N) ``` ## 線性同餘方法 LCG [wikipedia: 線性同餘方法](https://zh.wikipedia.org/wiki/線性同餘方法) 一種產生偽隨機數的方法 公式: $n_{i+1} \equiv (a \times n_i + b)\ (mod\ p)$ 特性: 當 $n_i \equiv b \times (1-a)^{-1}\ (mod\ p)$ 時,$n_i = n_{i+1} = n_{i+2} = ...$ proof: $$ \begin{aligned} x &\equiv (a \times x + b) &(mod\ p) \\ (x - a \times x) &\equiv b &(mod\ p) \\ (1-a) \times x &\equiv b &(mod\ p) \\ x &\equiv b \times (1-a)^{-1} &(mod\ p) \\ b \times (1-a)^{-1} &\equiv (a \times (b \times (1-a)^{-1}) + b) &(mod\ p) \end{aligned} $$ 以下破解參考[這篇文章](https://tailcall.net/posts/cracking-rngs-lcgs/) ### 破解 LCG 1: 不知加數 (b) 假設已知 $n_0$、$n_1$、$a$、$p$,不知 $b$ 公式: $b \equiv n_1 - a \times n_0\ (mod\ p)$ proof: $$ \begin{aligned} n_1 &\equiv a \times n_0 + b &(mod\ p) \\ b &\equiv n_1 - a \times n_0 &(mod\ p) \end{aligned} $$ ### 破解 LCG 2: 不知加數及乘數 (a, b) 假設已知 $n_0$、$n_1$、$n_2$、$p$,不知 $a$、$b$ 公式: $a \equiv (n_2 - n_1) \times (n_1 - n_0)^{-1} (mod\ p)$ 退化回上一題目 proof: $$ \begin{aligned} n_1 &\equiv a \times n_0 + b &(mod\ p) \\ n_2 &\equiv a \times n_1 + b &(mod\ p) \\ n_2 - n_1 &\equiv a \times (n_1 - n_0) &(mod\ p) &\leftarrow 2 式 - 1 式 \\ a &\equiv (n_2 - n_1) \times (n_1 - n_0)^{-1} &(mod\ p) \end{aligned} $$ ### 破解 LCG 3: 不知加數、乘數及模數 (a, b, p) 假設已知 $n_0$、$n_1$ ... $n_k$ ($k \ge 4$),不知 $a$、$b$、$p$ 令 $$ \begin{aligned} d_i &= n_{i+1} - n_i &(0 \le i \le k-1) \\ g_i &= d_{i+1} \times d_{i-1} - d_i \times d_i &(1 \le i \le k-2) \end{aligned} $$ 公式: $p = GCD(g_1, g_2, ... g_{k-2})$ 退化回上一題目 proof: $$ \begin{aligned} d_0 \equiv n_1 - n_0 & & & &(mod\ p)\\ d_1 \equiv n_2 - n_1 &\equiv (n_1 \times a + b) - (n_0 \times a + b) &\equiv a \times (n_1 - n_0) &\equiv a \times d_0 &(mod\ p) \\ d_2 \equiv n_3 - n_2 &\equiv (n_2 \times a + b) - (n_1 \times a + b) &\equiv a \times (n_2 - n_1) &\equiv a \times d_1 &(mod\ p) \\ d_3 \equiv n_4 - n_3 &\equiv (n_3 \times a + b) - (n_2 \times a + b) &\equiv a \times (n_3 - n_2) &\equiv a \times d_2 &(mod\ p) \\ ... \end{aligned} $$ $$ \begin{aligned} g_1 &\equiv d_2 \times d_0 - d_1 \times d_1 &\equiv ((a \times a \times d_0) \times d_0) - ((a \times d_0) \times (a \times d_0)) &\equiv 0 &(mod\ p) \\ g_2 &\equiv d_3 \times d_1 - d_2 \times d_2 &\equiv ((a \times a \times d_1) \times d_1) - ((a \times d_1) \times (a \times d_1)) &\equiv 0 &(mod\ p) \\ ... \end{aligned} $$ $g_1 = u_1 \times p$ $g_2 = u_2 \times p$ ... $GCD(g_1, g_2, ...) = p$ ## 線性 DES/AES 塊加密不能是線性的,否則可以利用一些公式直接破解,通常相關題目設計時會在 sbox 上搞鬼 ### 檢測線性 雖然以下圖片是 DES,但 AES 也是異曲同工之妙 ![](https://i.imgur.com/B3H0yC4.png) [[source]](https://crypto.stackexchange.com/questions/63693/s-box-and-its-linearity) $DES(k,m1) \oplus DES(k,m2) \oplus DES(k,m3) = DES(k,m1 \oplus m2 \oplus m3)$ ```python= import os from pwn import * def test_aes(cipher: AES) -> bool: key = os.urandom(16) m1 = os.urandom(16) m2 = os.urandom(16) m3 = os.urandom(16) res1 = xor(xor(cipher.encrypt(m1), cipher.encrypt(m2)), cipher.encrypt(m3)) res2 = cipher.encrypt(xor(xor(m1, m2), m3)) return res1 == res2 if __name__ == "__main__": cipher = AES(key, Sbox=sbox) isLinear = test_aes(cipher) ``` ### 利用線性特性 參考[這篇文章](https://crypto.stackexchange.com/questions/20228/consequences-of-aes-without-any-one-of-its-operations/70107#70107),可知線性 AES 可被 model 成 $ct = A \times pt + k$ 格式,只要求出 A 和 k 的值代表即可任意加解密 - $ct$, $pt$ 屬 GF(2) (i.e. bits) - $A$ 為一 $128 \times 128$ 的 GF(2) 矩陣,與 key 無關但與結構有關 - $k$ 為一長度 128 的 GF(2) 向量,與 key 相關 #### 求 A $p_0 = \begin{bmatrix} 0 \\ 0 \\ \vdots \\ 0 \end{bmatrix}$ $p_1 = \begin{bmatrix} 1 \\ 0 \\ \vdots \\ 0 \end{bmatrix}$ $p_2 = \begin{bmatrix} 0 \\ 1 \\ 0 \\ \vdots \\ 0 \end{bmatrix}$ $p_n = \dots$ $p_{128} = \begin{bmatrix} 0 \\ \vdots \\ 0 \\ 1 \end{bmatrix}$ $c_0 = A \times p_0 + k$ $c_1 = A \times p_1 + k$ $...$ $c_{128} = A \times p_{128} + k$ $\begin{aligned} C &= \begin{bmatrix} c_1 - c_0, c_2-c_0, \dots , c_{128} - c_0 \end{bmatrix} \\ &= \begin{bmatrix} (A \times p_1 + k) - (A \times p_0 + k), \dots , (A \times p_{128} + k) - (A \times p_0 + k) \end{bmatrix} \\ &= \begin{bmatrix} A \times (p_1 - p_0), \dots , A \times (p_{128} - p_0) \end{bmatrix} \\ &= A \times \begin{bmatrix} (p_1 - p_0), \dots , (p_{128} - p_0) \end{bmatrix} \\ &= A \times \begin{bmatrix} p_1, \dots , p_{128} \end{bmatrix} \\ &= A \times P \\ \end{aligned}$ #### 求 k $c = A \times p + k$ $k = c - A \times p$ #### 腳本 ```python= from pwn import xor from sage.all import GF, matrix, vector def bits2bytes(x: list) -> bytes: return bytes([int("".join(map(str, x[i:i+8])), 2) for i in range(0, len(x), 8)]) def bytes2bits(x: bytes) -> list: return list(map(int, "".join(map(lambda x: f"{x:08b}", x)))) def get_A(cipher: AES) -> matrix: # c = Ax+k # c-c0 = A(x-x0) # C = AX X = [] C = [] base = cipher.encrypt(bits2bytes([0]*128))[:16] # we don't need the cipher of padding for i in range(128): pt = [0]*i + [1] + [0]*(127-i) ct = cipher.encrypt(bits2bytes(pt))[:16] # we don't need the cipher of padding X.append(pt) C.append(bytes2bits(xor(ct,base))) mat_X = matrix(GF(2), X).transpose() mat_C = matrix(GF(2), C).transpose() mat_A = mat_X.solve_left(mat_C) return mat_A def get_k(cipher: AES, known_pt: bytes, known_ct: bytes) -> vector: vec_c = vector(bytes2bits(known_ct)) vec_x = vector(bytes2bits(known_pt)) vec_k = vec_c - mat_A * vec_x return vec_k def break_AES(cipher: AES, known_pt: bytes, known_ct: bytes, unknown_ct: bytes) -> bytes: pt = b"" mat_A = get_A(cipher) vec_k = get_k(cipher, known_pt, known_ct) # c = Ax+k # c-k = Ax for i in range(0, len(unknown_ct), 16): curr_ct = vector(bytes2bits(unknown_ct[i:i+16])) curr_pt = mat_A.solve_right(curr_ct - vec_k) pt += bits2bytes(curr_pt) return pt if __name__ == "__main__": cipher = AES(key, Sbox=sbox) plainText = bytes.fromhex(input("Input plaintext(hex): "))[:16] cipherText = bytes.fromhex(input("Input ciphertext(hex): "))[:16] ct_flag = bytes.fromhex(input("Input cipherflag(hex): ")) pt_flag = break_AES(cipher, plainText, cipherText, ct_flag) print(pt_flag) ``` ## DES 特殊性質 ### 補數 即 $E_k(P) = C$,$E_\bar{k}(\bar{P}) = \bar{C}$ [Data Encryption Standard - Wikipedia](https://en.wikipedia.org/wiki/Data_Encryption_Standard#Minor_cryptanalytic_properties) ### Weak key 在某些 key 的情況下,$E_{k_1}(E_{k_2}(P)) = P$ [Weak key - Wikipedia](https://en.wikipedia.org/wiki/Weak_key) [Recommendation for the Triple Data Encryption Algorithm (TDEA) Block Cipher](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-67r1.pdf) $\S$ 3.4.2 #### single key 即在 $k_1 = k_2$ 的情況下有前者的特性 - `0x0000000000000000` - `0x0101010101010101` - `0x1E1E1E1E0F0F0F0F` - `0x1F1F1F1F0E0E0E0E` - `0xE0E0E0E0F1F1F1F1` - `0xE1E1E1E1F0F0F0F0` - `0xFEFEFEFEFEFEFEFE` - `0xFFFFFFFFFFFFFFFF` #### semi-weak key 在 $k_1 \ne k_2$ 的情況 - `0x011F011F010E010E` + `0x1F011F010E010E01` - `0x01E001E001F101F1` + `0xE001E001F101F101` - `0x01FE01FE01FE01FE` + `0xFE01FE01FE01FE01` - `0x1FE01FE00EF10EF1` + `0xE01FE01FF10EF10E` - `0x1FFE1FFE0EFE0EFE` + `0xFE1FFE1FFE0EFE0E` - `0xE0FEE0FEF1FEF1FE` + `0xFEE0FEE0FEF1FEF1` #### possibly weak key 在某些 key 的情況下,切分出來的 subkey 只會產生 4 個 (而非 16 個) 以下為部分範例,詳細部分請參考[Recommendation for the Triple Data Encryption Algorithm (TDEA) Block Cipher](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-67r1.pdf) $\S$ 3.4.2 - `0x01011F1F01010E0E` - `0x1F01FEE00E01FEF1` - `0x1F1F01010E0E0101` - `0xE0E00101F1F10101` - `0xE0E01F1FF1F10E0E` - `0xFEFEE0E0FEFEF1F1` - ... ## 同態加密 (Paillier cryptosystem) ### 特性 #### encryption $n = p \times q$ $g = n + 1$ $r = rand(1, n)$ $enc \equiv g^m \times r^n \mod{n^2}$ 相當於是 $m^e \mod{n}$,此處 $e$ 為自然指數 #### decyption $n = p \times q$ $\phi = (p-1) \times (q-1)$ $\mu = \phi^{-1} \mod{n}$ $msg = \lfloor \frac{c^\phi \mod{n^2} - 1}{n} \rfloor \times \mu \mod{n}$ 相當於是 $log{(c)} \mod{n}$,此處 $log$ 為以自然指數為基底的 log ### additive homomorphic 同態加密有此特性: $f(x+y) = f(x) \cdot f(y)$ 因此可得出 $f(x+1) = f(x) \cdot f(1) = f(x) \cdot g$ 有時可搭配 RSA 的 Related Message Attack 進行破解 example: [prsa](https://github.com/maple3142/My-CTF-Challenges/blob/master/AIS3%20EOF%202025/prsa/README.md)