對稱密碼可以是區塊密碼,也可以是串流密碼。

  • 區塊密碼把大塊的明文位元與金鑰位元混合起來,產生同樣大小(通常 64 或 128 位元)的密文塊。
  • 串流密碼不混合明文與金鑰位元;它們從金鑰產生偽隨機位元,再把明文與這些偽隨機位元 XOR 來加密——方式與第 1 章的一次性密碼本相同。

串流密碼有時被人避而遠之,因為歷史上它們比區塊密碼脆弱、也更常被攻破——無論是業餘者設計的實驗性密碼,或是部署在數百萬人使用之系統(手機、Wi-Fi、大眾運輸智慧卡)中的密碼。

但那都是歷史了。雖然花了 20 年,我們現在已經知道如何設計安全的串流密碼,並信任它們保護藍牙連線、4G 行動通訊、TLS 連線等等。

本章脈絡#

  1. 串流密碼如何運作——金鑰流、nonce 的角色,以及有狀態 vs. 計數器式兩種架構。
  2. 硬體導向串流密碼——FSR、LFSR、NFSR,以及 Grain-128a 與(已破解的)A5/1。
  3. 軟體導向串流密碼——已崩壞的 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-1GMR-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