diff options
Diffstat (limited to 'frozen_deps/Cryptodome/IO/_PBES.py')
-rw-r--r-- | frozen_deps/Cryptodome/IO/_PBES.py | 229 |
1 files changed, 170 insertions, 59 deletions
diff --git a/frozen_deps/Cryptodome/IO/_PBES.py b/frozen_deps/Cryptodome/IO/_PBES.py index 9ee5385..75d8cde 100644 --- a/frozen_deps/Cryptodome/IO/_PBES.py +++ b/frozen_deps/Cryptodome/IO/_PBES.py @@ -31,15 +31,17 @@ # POSSIBILITY OF SUCH DAMAGE. # =================================================================== +import re + +from Cryptodome import Hash from Cryptodome import Random from Cryptodome.Util.asn1 import ( DerSequence, DerOctetString, DerObjectId, DerInteger, ) +from Cryptodome.Cipher import AES from Cryptodome.Util.Padding import pad, unpad -from Cryptodome.Hash import MD5, SHA1, SHA224, SHA256, SHA384, SHA512 -from Cryptodome.Cipher import DES, ARC2, DES3, AES from Cryptodome.Protocol.KDF import PBKDF1, PBKDF2, scrypt _OID_PBE_WITH_MD5_AND_DES_CBC = "1.2.840.113549.1.5.3" @@ -53,16 +55,14 @@ _OID_PBKDF2 = "1.2.840.113549.1.5.12" _OID_SCRYPT = "1.3.6.1.4.1.11591.4.11" _OID_HMAC_SHA1 = "1.2.840.113549.2.7" -_OID_HMAC_SHA224 = "1.2.840.113549.2.8" -_OID_HMAC_SHA256 = "1.2.840.113549.2.9" -_OID_HMAC_SHA384 = "1.2.840.113549.2.10" -_OID_HMAC_SHA512 = "1.2.840.113549.2.11" _OID_DES_EDE3_CBC = "1.2.840.113549.3.7" _OID_AES128_CBC = "2.16.840.1.101.3.4.1.2" _OID_AES192_CBC = "2.16.840.1.101.3.4.1.22" _OID_AES256_CBC = "2.16.840.1.101.3.4.1.42" - +_OID_AES128_GCM = "2.16.840.1.101.3.4.1.6" +_OID_AES192_GCM = "2.16.840.1.101.3.4.1.26" +_OID_AES256_GCM = "2.16.840.1.101.3.4.1.46" class PbesError(ValueError): pass @@ -103,6 +103,16 @@ class PbesError(ValueError): # prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1 # } # +# PBKDF2-PRFs ALGORITHM-IDENTIFIER ::= { +# {NULL IDENTIFIED BY id-hmacWithSHA1}, +# {NULL IDENTIFIED BY id-hmacWithSHA224}, +# {NULL IDENTIFIED BY id-hmacWithSHA256}, +# {NULL IDENTIFIED BY id-hmacWithSHA384}, +# {NULL IDENTIFIED BY id-hmacWithSHA512}, +# {NULL IDENTIFIED BY id-hmacWithSHA512-224}, +# {NULL IDENTIFIED BY id-hmacWithSHA512-256}, +# ... +# } # scrypt-params ::= SEQUENCE { # salt OCTET STRING, # costParameter INTEGER (1..MAX), @@ -111,6 +121,7 @@ class PbesError(ValueError): # keyLength INTEGER (1..MAX) OPTIONAL # } + class PBES1(object): """Deprecated encryption scheme with password-based key derivation (originally defined in PKCS#5 v1.5, but still present in `v2.0`__). @@ -141,21 +152,29 @@ class PBES1(object): cipher_params = {} if pbe_oid == _OID_PBE_WITH_MD5_AND_DES_CBC: # PBE_MD5_DES_CBC + from Cryptodome.Hash import MD5 + from Cryptodome.Cipher import DES hashmod = MD5 - ciphermod = DES + module = DES elif pbe_oid == _OID_PBE_WITH_MD5_AND_RC2_CBC: # PBE_MD5_RC2_CBC + from Cryptodome.Hash import MD5 + from Cryptodome.Cipher import ARC2 hashmod = MD5 - ciphermod = ARC2 + module = ARC2 cipher_params['effective_keylen'] = 64 elif pbe_oid == _OID_PBE_WITH_SHA1_AND_DES_CBC: # PBE_SHA1_DES_CBC + from Cryptodome.Hash import SHA1 + from Cryptodome.Cipher import DES hashmod = SHA1 - ciphermod = DES + module = DES elif pbe_oid == _OID_PBE_WITH_SHA1_AND_RC2_CBC: # PBE_SHA1_RC2_CBC + from Cryptodome.Hash import SHA1 + from Cryptodome.Cipher import ARC2 hashmod = SHA1 - ciphermod = ARC2 + module = ARC2 cipher_params['effective_keylen'] = 64 else: raise PbesError("Unknown OID for PBES1") @@ -167,7 +186,7 @@ class PBES1(object): key_iv = PBKDF1(passphrase, salt, 16, iterations, hashmod) key, iv = key_iv[:8], key_iv[8:] - cipher = ciphermod.new(key, ciphermod.MODE_CBC, iv, **cipher_params) + cipher = module.new(key, module.MODE_CBC, iv, **cipher_params) pt = cipher.decrypt(encrypted_data) return unpad(pt, cipher.block_size) @@ -231,49 +250,103 @@ class PBES2(object): if randfunc is None: randfunc = Random.new().read - if protection == 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC': + pattern = re.compile(r'^(PBKDF2WithHMAC-([0-9A-Z-]+)|scrypt)And([0-9A-Z-]+)$') + res = pattern.match(protection) + if res is None: + raise ValueError("Unknown protection %s" % protection) + + if protection.startswith("PBKDF"): + pbkdf = "pbkdf2" + pbkdf2_hmac_algo = res.group(2) + enc_algo = res.group(3) + else: + pbkdf = "scrypt" + enc_algo = res.group(3) + + aead = False + if enc_algo == 'DES-EDE3-CBC': + from Cryptodome.Cipher import DES3 key_size = 24 module = DES3 cipher_mode = DES3.MODE_CBC enc_oid = _OID_DES_EDE3_CBC - elif protection in ('PBKDF2WithHMAC-SHA1AndAES128-CBC', - 'scryptAndAES128-CBC'): + enc_param = {'iv': randfunc(8)} + elif enc_algo == 'AES128-CBC': key_size = 16 module = AES cipher_mode = AES.MODE_CBC enc_oid = _OID_AES128_CBC - elif protection in ('PBKDF2WithHMAC-SHA1AndAES192-CBC', - 'scryptAndAES192-CBC'): + enc_param = {'iv': randfunc(16)} + elif enc_algo == 'AES192-CBC': key_size = 24 module = AES cipher_mode = AES.MODE_CBC enc_oid = _OID_AES192_CBC - elif protection in ('PBKDF2WithHMAC-SHA1AndAES256-CBC', - 'scryptAndAES256-CBC'): + enc_param = {'iv': randfunc(16)} + elif enc_algo == 'AES256-CBC': key_size = 32 module = AES cipher_mode = AES.MODE_CBC enc_oid = _OID_AES256_CBC + enc_param = {'iv': randfunc(16)} + elif enc_algo == 'AES128-GCM': + key_size = 16 + module = AES + cipher_mode = AES.MODE_GCM + enc_oid = _OID_AES128_GCM + enc_param = {'nonce': randfunc(12)} + aead = True + elif enc_algo == 'AES192-GCM': + key_size = 24 + module = AES + cipher_mode = AES.MODE_GCM + enc_oid = _OID_AES192_GCM + enc_param = {'nonce': randfunc(12)} + aead = True + elif enc_algo == 'AES256-GCM': + key_size = 32 + module = AES + cipher_mode = AES.MODE_GCM + enc_oid = _OID_AES256_GCM + enc_param = {'nonce': randfunc(12)} + aead = True else: - raise ValueError("Unknown PBES2 mode") + raise ValueError("Unknown encryption mode '%s'" % enc_algo) - # Get random data - iv = randfunc(module.block_size) + iv_nonce = list(enc_param.values())[0] salt = randfunc(prot_params.get("salt_size", 8)) # Derive key from password - if protection.startswith('PBKDF2'): + if pbkdf == 'pbkdf2': + count = prot_params.get("iteration_count", 1000) - key = PBKDF2(passphrase, salt, key_size, count) + digestmod = Hash.new(pbkdf2_hmac_algo) + + key = PBKDF2(passphrase, + salt, + key_size, + count, + hmac_hash_module=digestmod) + + pbkdf2_params = DerSequence([ + DerOctetString(salt), + DerInteger(count) + ]) + + if pbkdf2_hmac_algo != 'SHA1': + try: + hmac_oid = Hash.HMAC.new(b'', digestmod=digestmod).oid + except KeyError: + raise ValueError("No OID for HMAC hash algorithm") + pbkdf2_params.append(DerSequence([DerObjectId(hmac_oid)])) + kdf_info = DerSequence([ DerObjectId(_OID_PBKDF2), # PBKDF2 - DerSequence([ - DerOctetString(salt), - DerInteger(count) - ]) + pbkdf2_params ]) - else: - # It must be scrypt + + elif pbkdf == 'scrypt': + count = prot_params.get("iteration_count", 16384) scrypt_r = prot_params.get('block_size', 8) scrypt_p = prot_params.get('parallelization', 1) @@ -289,12 +362,19 @@ class PBES2(object): ]) ]) + else: + raise ValueError("Unknown KDF " + res.group(1)) + # Create cipher and use it - cipher = module.new(key, cipher_mode, iv) - encrypted_data = cipher.encrypt(pad(data, cipher.block_size)) + cipher = module.new(key, cipher_mode, **enc_param) + if aead: + ct, tag = cipher.encrypt_and_digest(data) + encrypted_data = ct + tag + else: + encrypted_data = cipher.encrypt(pad(data, cipher.block_size)) enc_info = DerSequence([ DerObjectId(enc_oid), - DerOctetString(iv) + DerOctetString(iv_nonce) ]) # Result @@ -336,7 +416,7 @@ class PBES2(object): pbes2_params = DerSequence().decode(enc_algo[1], nr_elements=2) - ### Key Derivation Function selection + # Key Derivation Function selection kdf_info = DerSequence().decode(pbes2_params[0], nr_elements=2) kdf_oid = DerObjectId().decode(kdf_info[0]).value @@ -354,14 +434,16 @@ class PBES2(object): if left > 0: try: + # Check if it's an INTEGER kdf_key_length = pbkdf2_params[idx] - 0 left -= 1 idx += 1 except TypeError: + # keyLength is not present pass # Default is HMAC-SHA1 - pbkdf2_prf_oid = "1.2.840.113549.2.7" + pbkdf2_prf_oid = _OID_HMAC_SHA1 if left > 0: pbkdf2_prf_algo_id = DerSequence().decode(pbkdf2_params[idx]) pbkdf2_prf_oid = DerObjectId().decode(pbkdf2_prf_algo_id[0]).value @@ -379,57 +461,86 @@ class PBES2(object): else: raise PbesError("Unsupported PBES2 KDF") - ### Cipher selection + # Cipher selection enc_info = DerSequence().decode(pbes2_params[1]) enc_oid = DerObjectId().decode(enc_info[0]).value + aead = False if enc_oid == _OID_DES_EDE3_CBC: # DES_EDE3_CBC - ciphermod = DES3 + from Cryptodome.Cipher import DES3 + module = DES3 + cipher_mode = DES3.MODE_CBC key_size = 24 + cipher_param = 'iv' elif enc_oid == _OID_AES128_CBC: - # AES128_CBC - ciphermod = AES + module = AES + cipher_mode = AES.MODE_CBC key_size = 16 + cipher_param = 'iv' elif enc_oid == _OID_AES192_CBC: - # AES192_CBC - ciphermod = AES + module = AES + cipher_mode = AES.MODE_CBC key_size = 24 + cipher_param = 'iv' elif enc_oid == _OID_AES256_CBC: - # AES256_CBC - ciphermod = AES + module = AES + cipher_mode = AES.MODE_CBC + key_size = 32 + cipher_param = 'iv' + elif enc_oid == _OID_AES128_GCM: + module = AES + cipher_mode = AES.MODE_GCM + key_size = 16 + cipher_param = 'nonce' + aead = True + elif enc_oid == _OID_AES192_GCM: + module = AES + cipher_mode = AES.MODE_GCM + key_size = 24 + cipher_param = 'nonce' + aead = True + elif enc_oid == _OID_AES256_GCM: + module = AES + cipher_mode = AES.MODE_GCM key_size = 32 + cipher_param = 'nonce' + aead = True else: - raise PbesError("Unsupported PBES2 cipher") + raise PbesError("Unsupported PBES2 cipher " + enc_algo) if kdf_key_length and kdf_key_length != key_size: raise PbesError("Mismatch between PBES2 KDF parameters" " and selected cipher") - IV = DerOctetString().decode(enc_info[1]).payload + iv_nonce = DerOctetString().decode(enc_info[1]).payload # Create cipher if kdf_oid == _OID_PBKDF2: - if pbkdf2_prf_oid == _OID_HMAC_SHA1: - hmac_hash_module = SHA1 - elif pbkdf2_prf_oid == _OID_HMAC_SHA224: - hmac_hash_module = SHA224 - elif pbkdf2_prf_oid == _OID_HMAC_SHA256: - hmac_hash_module = SHA256 - elif pbkdf2_prf_oid == _OID_HMAC_SHA384: - hmac_hash_module = SHA384 - elif pbkdf2_prf_oid == _OID_HMAC_SHA512: - hmac_hash_module = SHA512 - else: + + try: + hmac_hash_module_oid = Hash.HMAC._hmac2hash_oid[pbkdf2_prf_oid] + except KeyError: raise PbesError("Unsupported HMAC %s" % pbkdf2_prf_oid) + hmac_hash_module = Hash.new(hmac_hash_module_oid) key = PBKDF2(passphrase, salt, key_size, iteration_count, hmac_hash_module=hmac_hash_module) else: key = scrypt(passphrase, salt, key_size, iteration_count, scrypt_r, scrypt_p) - cipher = ciphermod.new(key, ciphermod.MODE_CBC, IV) + cipher = module.new(key, cipher_mode, **{cipher_param:iv_nonce}) # Decrypt data - pt = cipher.decrypt(encrypted_data) - return unpad(pt, cipher.block_size) + if len(encrypted_data) < cipher.block_size: + raise ValueError("Too little data to decrypt") + + if aead: + tag_len = cipher.block_size + pt = cipher.decrypt_and_verify(encrypted_data[:-tag_len], + encrypted_data[-tag_len:]) + else: + pt_padded = cipher.decrypt(encrypted_data) + pt = unpad(pt_padded, cipher.block_size) + + return pt |