如何生成連續的流密碼密鑰?
我發現了我正在研究的分佈式模擬系統的一個弱點,我正在尋找一些關於如何修復它的建議。
客戶端最初使用 Diffie-Hellman 與登錄伺服器協商身份驗證令牌。然後他們依次連接到他們需要的每個模擬節點(單獨的主機),使用令牌作為數據加密的密鑰。
問題在於,對於單個客戶端,每個節點都使用相同的密鑰(即令牌數據)。
我正在考慮每次使用令牌時散列 n+1 次,以便每次生成一個新密鑰。不幸的是,我沒有選擇為客戶端想要訪問的每個節點重新協商一個全新的密鑰。
這聽起來不錯,至少暫時修復?
感謝您的任何建議。
乾杯!
我可以看到您關於讓會話密鑰的建議存在許多問題 $ sk_{n,c} $ 對於節點 $ n $ 和客戶 $ c $ 是 $ sk_{n,c} = h^{n+1}(masterkey_c) $ , 在哪裡 $ masterkey_c $ 就是你所說的“令牌”:
- 您應該使用不同的密鑰進行加密和完整性,並為客戶端 -> 節點方向和節點 -> 客戶端方向使用不同的密鑰,每個節點和客戶端組合總共有四個密鑰。
- 鍵不是獨立的。如果對手能夠破壞節點 $ n $ 並獲取該節點的會話密鑰,然後是每個節點的會話 $ n+k $ 和 $ k > 0 $ 也會受到微不足道的妥協。
- 沒有簡單的方法來談判一個新的 $ sk_{n,c} $ 如果客戶端之間的連接 $ c $ 和節點 $ n $ 被丟棄。客戶將不得不協商一個新的 $ masterkey_c $ .
我想你已經想出了一種安全分發的方法 $ masterkey_c $ 從登錄伺服器到節點。問題正是您需要哪些安全功能。我假設客戶端和節點之間的通信是雙向的,並且您的應用程序需要保護免受重放攻擊以及能夠輕鬆地重新協商任何客戶端和節點對的會話密鑰而無需重新協商主節點客戶的鑰匙。
首先,您需要一個密鑰派生函式 $ KDF(keymaterial,label,nonce) $ . 如果您擁有的唯一加密原語是流密碼和散列,則您首先必須實現HMAC,因為稍後它將用於協議。其次,您可以按如下方式構造 KDF:
$ KDF(keymaterial,label,nonce) = HMAC(keymaterial,label|nonce) $
在哪裡 $ | $ 表示連接。
第三,您需要一個不可預測的偽隨機位生成器。由於客戶端正在與登錄伺服器執行 Diffie-Hellman 握手,因此它必須已經有一個。如果節點無法訪問登錄伺服器具有的相同功能,它們可能會隨機獲得一個 $ seed $ 由登錄伺服器發送給他們並使用以下內容:
$ DRBG(seed,ctr) = KDF(seed,“drbg”,I2OS(ctr)), ctr = ctr + 1 $
客戶端/節點主密鑰
伺服器端,登錄伺服器進行如下計算,並將結果發送給對應的節點。客戶端可能會在連接到節點之前立即執行計算。關鍵材料 $ masterkey_c $ 是 Diffie-Hellman 握手的輸出:
$ masterkey_{c,n} = KDF(masterkey_c,“mast”,I2OS(n)) $
客戶端/節點握手
輸入: $ masterkey_{c,n} $
輸出:消息計數器 c->n $ Ctr_{c,n} $ 消息計數器 n->c $ Ctr_{n,c} $ , 會話密鑰 $ ek_{c,n} $ , $ ek_{n,c} $ , $ ak_{c,n} $ , $ ak_{n,c} $ .
**注意:**為簡單起見,我假設您使用的是有狀態流密碼,並且加密密鑰 $ ek_{c,n} $ 和 $ ek_{n,c} $ 可以看作是通過每次呼叫加密函式來更新的流密碼狀態 $ E(keystate,text) $ .
- 客戶端和節點都將兩個消息計數器設置為 $ 0 $ .
- 客戶端生成一個隨機數 $ nonce_c $ 並將其發送到節點。
- 節點生成一個隨機數 $ nonce_n $ 並將其發送給客戶端。
- 兩者都計算 $ ek_{c,n} = KDF(masterkey_{c,n},“ekcn”,h(nonce_c)|h(nonce_n)) $ , $ ek_{n,c} = KDF(masterkey_{c,n},“eknc”,h(nonce_c)|h(nonce_n)) $ , $ ak_{c,n} = KDF(masterkey_{c,n},“akcn”,h(nonce_c)|h(nonce_n)) $ , $ ak_{n,c} = KDF(masterkey_{c,n},“aknc”,h(nonce_c)|h(nonce_n)) $ .
- 客戶計算 $ HMAC(ak_{c,n},I2OS(Ctr_{c,n})|E(ek_{c,n},“verify”)), Ctr_{c,n} = Ctr_{c,n} + 1 $ 並將其發送到節點。如果節點失敗,則斷開連接。
- 節點計算 $ HMAC(ak_{n,c},I2OS(Ctr_{n,c})|E(ek_{n,c},“verify”)), Ctr_{n,c} = Ctr_{n,c} + 1 $ 並將其發送到節點。如果失敗,客戶端將斷開連接。
握手協議中可能應該有一些安全機制,以防止客戶端和節點在發生故障時無限期地重複它。例如,他們可以嘗試完成 3 次,然後放棄,客戶端要麼將該特定節點標記為不可訪問,要麼返回登錄伺服器協商一個新的 $ masterkey_c $ . 如果客戶端無法完成與多個節點的握手,則客戶端僅應執行後者。
批量數據傳輸
輸入: $ pt $
輸出: $ ct|mac $
客戶端到節點:
- $ ct = E(ek_{c,n},pt) $
- $ mac = HMAC(ak_{c,n},I2OS(Ctr_{c,n})|ct) $
- $ Ctr_{c,n} = Ctr_{c,n} + 1 $
客戶端發送 $ ct|mac $ 到節點,節點計算 $ HMAC(ak_{c,n},I2OS(Ctr_{c,n})|ct) $ 並驗證它是否匹配 $ mac $ ,如果沒有,則斷開連接,並遞增 $ Ctr_{c,n} $ 並解密 $ ct $ 除此以外。
節點到客戶端:
- $ ct = E(ek_{n,c},pt) $
- $ mac = HMAC(ak_{n,c},I2OS(Ctr_{n,c})|ct) $
- $ Ctr_{n,c} = Ctr_{n,c} + 1 $
節點發送 $ ct|mac $ 給客戶,客戶計算 $ HMAC(ak_{n,c},I2OS(Ctr_{n,c})|ct) $ 並驗證它是否匹配 $ mac $ ,如果沒有,則斷開連接,並遞增 $ Ctr_{n,c} $ 並解密 $ ct $ 除此以外。
希望這可以幫助
不,這不是一個好主意。
原因是,如果您曾經在流密碼中重複使用密鑰,攻擊者可以對已知的明文進行異或運算以獲取密鑰流。
是的,您的修復是一種改進,但是您從完全不安全開始,並且您已經將其推高到幾乎完全不安全。
登錄令牌可能很容易被破解,即使不可猜測,攻擊者只需從它開始並繼續散列以獲得測試密鑰。整個搜尋空間可能小於 100。它很少會超過 1000,因此真正想要破解你的人會在獲得正確的密鑰之前等待幾毫秒。
你沒有 /dev/random 的等價物嗎?還是 /dev/urandom?還是什麼?如果您從具有良好關鍵價值的東西開始,那麼您基本上還可以。
您從哪裡獲得您的 DH 的 x(如 g^x),或者這也不是隨機的?坦率地說,如果您遇到困難,最好對 x 進行雜湊處理,而不是對您的令牌進行雜湊處理——或者對兩者進行雜湊處理,或者使用 X 作為您的 HMAC 密鑰對令牌進行 HMAC 雜湊處理。無論您從哪裡獲取 X,都可以使用它來獲取您的密鑰。