3.1 一次性密碼本與完美保密#
概念說明#
一次性密碼本(One-Time Pad, OTP)是 1917 年由 Vernam 提出、後經香農證明「無法更強」的加密法。規則簡單到不像話:
- 金鑰 K 是一串真隨機位元,長度等於明文。
- 加密:
C = M ⊕ K(逐位元互斥或)。 - 解密:
M = C ⊕ K(⊕ 的對合性:(M⊕K)⊕K = M)。
完美保密(perfect secrecy)的資訊理論定義:對任何明文 m 與密文 c,
Pr[M = m | C = c] = Pr[M = m]
即「看到密文」與「完全沒看到密文」,對猜測明文的機率完全一樣——密文不攜帶任何關於明文的資訊。香農證明:要達成完美保密,金鑰的不確定性(熵)必須至少等於明文的不確定性,因此 OTP 是資訊理論上的最優,不可能有更省金鑰的完美保密系統。
直覺理解:對攻擊者而言,同一個密文 C 配上任意猜想的金鑰 K' 都能解出任意明文 M',且每個 M' 恰有一個 K' 與之對應——所有明文的可能性完全均等,毫無線索。
演算法步驟#
- 用密碼學安全隨機源(如硬體亂數、放射性衰變)產生與明文等長的金鑰 K。
- 透過絕對安全的管道(外交郵袋、親手交付)把 K 交給對方。
- 加密:逐位元
C = M ⊕ K,用完即毀,絕不重用。 - 解密:
M = C ⊕ K,同樣用完即毀。
Python 實作#
import os # os.urandom 是密碼學安全隨機源
def otp_encrypt(message: bytes, key: bytes) -> bytes:
assert len(key) >= len(message), "金鑰長度必須 >= 訊息長度"
return bytes(m ^ k for m, k in zip(message, key))
def otp_decrypt(ciphertext: bytes, key: bytes) -> bytes:
return otp_encrypt(ciphertext, key) # ⊕ 自反
msg = "Attack at dawn!".encode()
key = os.urandom(len(msg)) # 真隨機、等長
ct = otp_encrypt(msg, key)
print(ct) # 每次執行都不同、看似雜訊
print(otp_decrypt(ct, key)) # b'Attack at dawn!'
安全性分析:三個致命的實務條件#
OTP 在數學上完美,實務上卻幾乎不可用,因為三個條件任何一個破功都毀滅一切:
- 金鑰必須真隨機:若用 PRNG 產生,安全性等同該 PRNG(那就不如直接用 AES)。歷史上蘇聯間諜網曾重用同一批密碼本頁面,美國的 VENONA 計畫據此破解了數千份電報——重用 = C1⊕C2 = M1⊕M2,兩把鑰匙的互斥或直接消去金鑰。
- 金鑰等長且絕不重用:這表示傳送 1 GB 檔案要先安全地配送 1 GB 金鑰,且每次都要新的——金鑰配送問題比被保護的訊息更大。二戰後只有熱線(如莫斯科—華盛頓熱線)等高價值低流量場景真正使用。
- 金鑰配送本身必須完美:配送管道若不安全,整個系統的弱點就等於管道。
現代定位:OTP 的價值不在使用,而在樹立理論上限——它告訴我們「只靠計算假設」之外的絕對安全是可能的,也逼我們承認:一切實用系統(AES、ChaCha20)都是「計算安全」的妥協,其金鑰流本質上是「可重用的假 OTP」。量子密鑰分發(QKD)與後量子時代的討論(第 10 章)也經常以 OTP 為理論終點。
練習#
- 手算:明文
HELLO與金鑰XMCKL做 OTP 加密(A=0),寫出每一步。 - 兩段密文 C1、C2 來自同一把 OTP 金鑰。證明
C1 ⊕ C2 = M1 ⊕ M2,並寫程式示範:用一段已知英文 M1 與 C1⊕C2,推測 M2 的片段(crib dragging)。 - 討論題:若金鑰是「真隨機但只有明文長度的一半、重複兩次使用」,這個系統還完美保密嗎?金鑰熵要多少才夠?