對稱密碼可以是區塊密碼,也可以是串流密碼。
- 區塊密碼把大塊的明文位元與金鑰位元混合起來,產生同樣大小(通常 64 或 128 位元)的密文塊。
- 串流密碼則不混合明文與金鑰位元;它們從金鑰產生偽隨機位元,再把明文與這些偽隨機位元 XOR 來加密——方式與第 1 章的一次性密碼本相同。
串流密碼有時被人避而遠之,因為歷史上它們比區塊密碼脆弱、也更常被攻破——無論是業餘者設計的實驗性密碼,或是部署在數百萬人使用之系統(手機、Wi-Fi、大眾運輸智慧卡)中的密碼。
但那都是歷史了。雖然花了 20 年,我們現在已經知道如何設計安全的串流密碼,並信任它們保護藍牙連線、4G 行動通訊、TLS 連線等等。
本章脈絡#
- 串流密碼如何運作——金鑰流、nonce 的角色,以及有狀態 vs. 計數器式兩種架構。
- 硬體導向串流密碼——FSR、LFSR、NFSR,以及 Grain-128a 與(已破解的)A5/1。
- 軟體導向串流密碼——已崩壞的 RC4(在 WEP 與 TLS 中),以及現代的 Salsa20。
常見錯誤#
串流密碼可能出錯的地方很多,從脆弱不安全的設計,到強演算法的錯誤實作都有。
nonce 重複使用#
串流密碼最常見的失效是一個業餘錯誤:同一把金鑰配上重複使用的 nonce。
這會產生相同的金鑰流,讓人只要把兩段密文 XOR 起來就能破解加密——金鑰流隨之消失,剩下的就是兩段明文的 XOR。
舊版的 Microsoft Word 與 Excel 為每份文件使用唯一的 nonce,但文件被修改後 nonce 並未更換。
結果是:某份文件舊版本的明文與密文,可以被用來解密後來的加密版本。
如果連微軟都犯這種大錯,你可以想像這個問題有多普遍。
2010 年代設計的某些串流密碼試圖以「抗誤用」(misuse-resistant)建構來緩解 nonce 重用的風險——也就是即使 nonce 被用了兩次仍保持安全的密碼。
但達到這種安全等級是有效能代價的,第 8 章的 SIV 模式會談到。
崩壞的 RC4 實作#
RC4 本來就弱,但若你盲目最佳化它的實作,它會變得更弱。
以 2007 年 Underhanded C Contest(一個「寫出看似良性、實則含惡意功能之程式碼」的非正式競賽)的一則參賽作品為例。
實作 RC4 演算法中 swap(S[i], S[j]) 的天真做法是:
buf = S[i]
S[i] = S[j]
S[j] = buf這顯然可行,但需要建立一個新變數 buf。為了避免這點,程式設計師常用 XOR 交換技巧:
x = x ⊕ y
y = x ⊕ y
x = x ⊕ y這個技巧有效,因為第二行把 y 設為 x ⊕ y ⊕ y = x,第三行把 x 設為 x ⊕ y ⊕ x ⊕ y ⊕ y = y。
用這個技巧實作 RC4 會得到:
# define TOBYTE(x) (x) & 255
# define SWAP(x,y) do { x^=y; y^=x; x^=y; } while (0)
static unsigned char S[256];
static int i=0, j=0;
void init(char *passphrase) {
int passlen = strlen(passphrase);
for (i=0; i<256; i++)
S[i] = i;
for (i=0; i<256; i++) {
j = TOBYTE(j + S[TOBYTE(i)] + passphrase[j % passlen]);
SWAP(S[TOBYTE(i)], S[j]);
}
i = 0; j = 0;
}
unsigned char encrypt_one_byte(unsigned char c) {
int k;
i = TOBYTE(i+1);
j = TOBYTE(j + S[i]);
SWAP(S[i], S[j]);
k = TOBYTE(S[i] + S[j]);
return c ^ S[k];
}先別往下讀,試著找出這段 XOR 交換的問題。
XOR 交換不會讓狀態保持不變,而是把
S[i]設為S[i] ⊕ S[i] = 0。效果是:每當金鑰排程或加密過程中
i等於j,狀態就有一個位元組被歸零,最終導致全零狀態,因而產生全零金鑰流。例如處理了 68 KB 資料之後,256 位元組狀態中大多數位元組已經是零,輸出的金鑰流長這樣:
00 00 00 00 00 00 00 53 53 00 00 00 00 00 00 00 00 00 00 00 13 13 00 5c 00 a5 00 00 ...
這裡的教訓是:不要過度最佳化你的密碼學實作。
在密碼學中,清晰與信心永遠勝過效能。
弱密碼被燒進硬體#
當一個密碼系統失去安全性,有些系統能迅速反應——遠端靜默更新受影響的軟體(如某些付費電視系統),或發布新版本並提示使用者升級(如行動應用)。有些系統就沒那麼幸運,得在升級到安全版本之前繼續使用已被攻破的密碼系統一段時間——衛星電話就是如此。
2000 年代初,美國與歐洲的電信標準化機構(TIA 與 ETSI)共同制定了兩個衛星電話通訊標準。衛星電話像手機,只是訊號經由衛星而非地面基地台傳送;優點是幾乎在世界任何地方都能使用,缺點則是價格、品質、延遲——以及事實證明的,安全性。
GMR-1 與 GMR-2 是多數商業廠商(如 Thuraya 與 Inmarsat)採用的兩個衛星電話標準,兩者都包含用來加密語音通訊的串流密碼:
- GMR-1 的密碼是硬體導向的,由四個 LFSR 組合而成,類似 A5/2——那個 2G 行動標準中刻意設計得不安全、針對非西方國家的密碼。
- GMR-2 的密碼是軟體導向的,有 8 位元組的狀態並使用 S-box。
這個故事提醒我們:串流密碼過去比區塊密碼容易破解,也更容易被蓄意破壞。
為什麼?因為如果你刻意設計一個弱的串流密碼,當瑕疵被發現時,你仍然可以把它歸咎於「串流密碼本來就比較弱」,並否認任何惡意企圖。
延伸閱讀#
- 從 eSTREAM 競賽的檔案庫開始:http://www.ecrypt.eu.org/stream/project.html ↗,那裡有數百篇關於串流密碼的論文,包括 30 多個候選者的細節與大量攻擊。最有趣的攻擊包括相關攻擊、代數攻擊與立方攻擊——前兩類特別參考 Courtois 與 Meier 的工作,立方攻擊參考 Dinur 與 Shamir。
- 關於 RC4:參考 Paterson 團隊在 http://www.isg.rhul.ac.uk/tls/ ↗ 上關於 RC4 用於 TLS 與 WPA 之安全性的研究。也可看看 Spritz——1980 年代設計 RC4 的 Rivest 於 2014 年創造的類 RC4 密碼。
- Salsa20 的遺產同樣值得關注:串流密碼 ChaCha 與 Salsa20 相似,但核心置換略有不同,後來被用於雜湊函式 BLAKE(見第 6 章)。這些演算法都運用了 Salsa20 使用平行化指令的軟體實作技巧,詳見 https://cr.yp.to/snuffle.html ↗。