2011-10-27 47 views
2

我需要在各種操作模式,允許crypto agility圍繞對稱密碼(AES,DES等)在Python中編寫一套包裝。具體來說,調用包裝器的代碼不需要知道實際上保護數據的內容,因此它可以動態更改,因此它可以動態更改。Pythonic解決密碼敏捷,避免違反Liskov的替代原則

基本上,以下內容應該成立(無論這些對象是方法,獨立函數還是別的東西)

foo = MagicalEncryptor() 
foo.ciphertext = foo.encrypt(data) 
key = foo.key 
bar = MagicalEncryptor() 
bar.key = key 
data = bar.decrypt(ciphertext) 

問題是,根據使用的模式,最終的密文將是不同的。對於GCM模式,它可能是(MODE_CBC,IV,密文)或(MODE_GCM,IV,密文,mac)。

這非常明顯違反了Liskov substitution principle,因爲它使參數解密協變。如果調用者持有正常情況下適用於GCM模式的通用magicalEncryptor接口的實例,則無法將其傳遞給ECB模式的實例。

什麼是一個很好的pythonic解決方案呢? (或者答案根本就不在乎?)對於我特別需要做的事情,它應該同時適用於2.7和3.0,但我對兩種解決方案都感興趣。

此外,鍵必須有一個短的表示法作爲一個比特流(可能最多128或256位)。這意味着在混合加密方案,其中,例如,一個可以發送(RSA_ENC(公鑰,symetric_key_as_message)|| AES(symetric_key_as_message,actual_message)的使用

回答

1

繼柯克,但在最後一分鐘剝落,讓多態性關鍵在於選擇正確的種類解密的:

foo = MagicalEncryptor() 
foo.ciphertext = foo.encrypt(data) 
key = foo.key 

bar = key.decryptor() 
data = bar.decrypt(ciphertext) 

它可以創建適當的解密,並通過自身或任何:密鑰和解密器的創造之間的協議是私有的。

我甚至可能重新安排這樣的事情:

key = createMagicalKey() 

foo = key.encryptor() 
ciphertext = foo.encrypt(data) 

bar = key.decryptor() 
data = bar.decrypt(ciphertext) 

當然,那麼它只是一個簡單的步驟:

key = createMagicalKey() 

ciphertext = key.encrypt(data) 

data = key.decrypt(ciphertext) 
+0

一個體面的解決方案。不幸的是,正如我剛剛澄清的那樣,密鑰基本上必須具有相當短的比特流表示,因爲它們需要使用類似RSA \ DSA \的更加奇特的東西進行加密。最多可能是128或256位。 – imichaelmiers

+0

我不認爲我的建議與短比特流表示不兼容;這是一個API設計,而不是協議格式。我並不是說你必須發送魔法鑰匙的字節碼或任何東西 - 只是某種標籤說明它是什麼樣的魔法鑰匙。只要接收者知道與您相同的一組標籤,就是說。 –

0

方法一:強類型的加密數據:

class GCMData(object): 
    def __init__(self, data): self.data = data 

    def __str__(self): return self.data 
    __repr__ = __str__ 


class MagicalGCMEncryptor(object): 
    def encrypt(self, data): 
     return GCMData(self._encrypt(data)) 

    def decrypt(self, dataobj): 
     if not isinstance(dataobj, GCMData): 
      raise ValueError('Only decrypts GCM data') 

方法二:反轉流量控制不要做這樣的數據進行加密類使存儲加密數據的數據對象:

class GCMEncryptedData(object): 
    def __init__(self, data): 
     self.data = [... do something with data] 

    def __str__(self): return self.data 
    __repr__ = __str__ 

    def decrypt(self): 
     return [... do something with self.data] 
+0

我不確定顛倒流量控制可以解決問題。你仍然需要注意傳遞正確的密鑰。 –

+0

解決方案1似乎只是明確地解釋了liskov替換問題。 MagicalGCMEncryptor不是MagicalEncryptor的子類。 解決方案2是一個途徑,但正如@TomAnderson所說,它會導致一些密鑰問題。此外,它使得與其他語言的交互變得困難,並且3)對數據進行反序列化是一個有趣的問題。我們當然不能使用pickle,因爲如果我們首先信任數據,那麼對它進行加密將是沒有意義的。 – imichaelmiers

0

「這很明顯違反了里氏替換原則,因爲它使參數來解密covariant,如果調用者持有恰好用於GCM模式的通用magicalEncryptor接口的實例,則不能將其傳遞給ECB模式的實例。「

也不應該。加密不能是獨立於模式的黑盒子。是的,你可以用AES-256替換TDES而不用改變很多更高級別的邏輯,但是對於改變加密模式來說並不是這樣。將模式看作協議,將算法看作引擎。協議很複雜,因爲它需要自己的特殊錯誤報告,失敗機制,初始化過程,相關數據,狀態信息等。相比之下,交換算法引擎很容易。

例如,使用GCM時,您需要注意不要加密太多的消息,並且爲了保持mac的加密強度而更快速地旋轉密鑰。無論是TDES-GCM還是AES-256-GCM,情況都是如此。這意味着您應該記錄已加密的消息數 - 即使這是一個估計值,例如「使用模式Y時每隔X天重新輸入一次密鑰」(儘管您確實在意數據處理的數量。)

您在ECB中沒有這些擔憂(在實踐中),但您對信息泄漏有一個完全不同的(更加困難的)擔憂。

CBC模式有它自己的使用陷阱,特別是如果您也使用CBC-MAC。根據所使用的身份驗證方法,當MAC失敗時需要注意報告錯誤消息 - 在某些情況下,MAC失敗應導致密鑰(例如會話密鑰)被破壞,但在其他情況下, MAC只會導致重置協議運行(保持您的身份驗證密鑰集)。或者你可能可以重播信息 - 這取決於你如何建立你的協議和你使用的算法。

如果您對存儲的數據使用調整加密模式,則需要關心數據駐留在磁盤上的位置,並且如果覆蓋某個塊,您將重新使用相同的調整鍵和位置值。但是如果你正在加密一個頻道,你永遠不會重複使用具有相同會話密鑰的消息計數器。適用於加密現場數據的加密模式與適合加密頻道的加密模式沒有重疊。它們不能互換。

所有這些都違反了Liskov替換原則,除非您嘗試實現可專用於所有可能的數據保護協議的通用「安全」對象。

此外,你會從來沒有 *永遠*在兩種不同的模式或兩個不同的算法引擎使用相同的密鑰。

那麼爲什麼設計代碼的方式允許一個關鍵對象的實例專用於多個算法實現?現在,您的安全團隊將會重新開始您的代碼,並確保此通用性不會爲攻擊者打開任何漏洞,使您的應用程序以錯誤的方式使用密鑰而濫用密鑰。您需要返回並確保在實例化密鑰時,完全指定將在密鑰的構造函數中使用的模式和算法,這可以回到密鑰長度的問題。任何隨後嘗試使用與其實例化的算法或模式不同的密鑰都應該導致嚴重錯誤。

但是在這種情況下,如果每次底層系統採用新算法時,用戶或應用程序需要創建新密鑰和/或重新生成現有數據,那麼「算法敏捷性」是指什麼?