Aes
將 HMAC 與不需要填充的 AES 模式一起使用
我正在嘗試將 HMAC 與不需要任何填充的 AES 模式一起使用。雖然我知道類似
AES-GCM
並且AES-EAX
已經提供身份驗證的模式,但我想知道 HMAC 是否適用於不提供任何身份驗證的模式。這是我嘗試過的:編碼
from Cryptodome.Cipher import AES import hmac class DecryptionError(Exception): pass class AESNonAEAD: def __init__(self, is_encrypting, key, mode, iv_or_nonce, digestmod='sha256'): self._is_encrypting = is_encrypting # Use KDF to derive key material from given key: # length of crp == length of key # length of authkey == (digest size of hashalgo for HMAC // 8) crpkey, authkey = derive_key_material(key, digestmod) self._auth = hmac.new(authkey, digestmod=digestmod) self._cipher = AES.new(key, mode, iv_or_nonce) if is_encrypting: self._update = self._cipher.encrypt else: self._update = self._cipher.decrypt self._auth.update(iv_or_nonce) self._updated = False self._len_ct = 0 self._len_aad = 0 def authenticate(self, data): if self._updated: raise ValueError('update has already been called') self._auth.update(data) self._len_aad += len(data) def _pad_aad(self): # pad to a multiple of 16 bytes if self._len_aad & 0x0F: self._auth.update(bytes(16 - (self._len_aad & 0x0F))) def update(self): if not self._updated: self._pad_aad() self._updated = True if not self._is_encrypting: self._auth.update(data) res = self._update(data) self._len_ct += len(res) if self._is_encrypting: self._auth.update(res) return res def finalize(self, tag=None): if not self._is_encrypting and tag is None: raise ValueError('tag is required') # pad to a multiple of 16 bytes if self._len_ct & 0x0F: self._auth.update(bytes(16 - (self._len_ct & 0x0F))) # include length of aad and ciphertext self._auth.update(self._len_aad.to_bytes(8, 'little')) self._auth.update(self._len_ct.to_bytes(8, 'little')) if self._is_encrypting: return if not hmac.compare_digest(tag, self._auth.digest()): raise DecryptionError def calcuate_tag(self): if self._is_encrypting: return self._hmac.digest()
用法
加密
from Cryptodome.Cipher import AES key = os.urandom(32) iv = os.urandom(16) enc = AESNonAEAD(True, key, AES.MODE_CFB, iv) # or MODE_CTR enc.authenticate(b'yes this') encdata = enc.update(b'no not this') enc.finalize() tag = enc.calculate_tag()
解密
dec = AESNonAEAD(False, key, AES.MODE_CFB, iv) # or MODE_CTR dec.authenticate(b'yes this') decdata = dec.update(encdata)
問題:
- 這種實現是否可以接受?
- 這是使用 HMAC 的正確方法嗎?是否有必要為非填充密碼模式填充 HMAC?
- 這個相同的方案可以與 Camellia 密碼一起使用嗎?
如果我理解正確,您對 HMAC 的輸入是密文 $ c $ , 用空字節填充(到 16 字節的倍數):
# pad to a multiple of 16 bytes if self._len_ct & 0x0F: self._auth.update(bytes(16 - (self._len_ct & 0x0F)))
作為一個具體的例子,假設密文是 $ c= $
deadbeef00
,那麼您將計算 HMAC 標記為 $ t = $ HMAC(deadbeef0000...00
),16 個字節。但這也是不同密文的有效 HMAC 標籤 $ c’= $deadbeef
也 $ c’ = $deadbeef000000
等我希望你能明白為什麼這會破壞經過身份驗證的加密安全屬性。我不確定您為什麼要以這種方式填充/編碼 HMAC 輸入,但我的猜測是您正試圖將密文和相關數據“序列化”為提供給 HMAC 的單個字元串。這是身份驗證的一個常見陷阱:您想對一對事物進行身份驗證 $ (x,y) $ ,所以你驗證字元串 $ x | y $ . 但是很多對 $ (x’,y’) $ 映射到同一個字元串 $ x|y $ (想想長度的情況 $ x $ , $ y $ 是可變的)!為了正確進行身份驗證,您需要對這對進行明確的編碼 $ (x,y) $ 成一個字元串。一種好方法是以某種方式包括 $ x $ 作為此編碼的一部分。
**編輯:**你似乎也從來沒有使用過
self._cipher
?