diff options
Diffstat (limited to 'frozen_deps/Cryptodome/IO')
-rw-r--r-- | frozen_deps/Cryptodome/IO/PKCS8.py | 49 | ||||
-rw-r--r-- | frozen_deps/Cryptodome/IO/PKCS8.pyi | 15 | ||||
-rw-r--r-- | frozen_deps/Cryptodome/IO/_PBES.py | 229 | ||||
-rw-r--r-- | frozen_deps/Cryptodome/IO/_PBES.pyi | 15 |
4 files changed, 208 insertions, 100 deletions
diff --git a/frozen_deps/Cryptodome/IO/PKCS8.py b/frozen_deps/Cryptodome/IO/PKCS8.py index d02aed9..3041545 100644 --- a/frozen_deps/Cryptodome/IO/PKCS8.py +++ b/frozen_deps/Cryptodome/IO/PKCS8.py @@ -53,44 +53,29 @@ def wrap(private_key, key_oid, passphrase=None, protection=None, Args: - private_key (byte string): + private_key (bytes): The private key encoded in binary form. The actual encoding is algorithm specific. In most cases, it is DER. key_oid (string): The object identifier (OID) of the private key to wrap. - It is a dotted string, like ``1.2.840.113549.1.1.1`` (for RSA keys). + It is a dotted string, like ``'1.2.840.113549.1.1.1'`` (for RSA keys) + or ``'1.2.840.10045.2.1'`` (for ECC keys). - passphrase (bytes string or string): + Keyword Args: + + passphrase (bytes or string): The secret passphrase from which the wrapping key is derived. Set it only if encryption is required. protection (string): The identifier of the algorithm to use for securely wrapping the key. - The default value is ``PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC``. + Refer to :ref:`the encryption parameters<enc_params>` . + The default value is ``'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'``. prot_params (dictionary): - Parameters for the protection algorithm. - - +------------------+-----------------------------------------------+ - | Key | Description | - +==================+===============================================+ - | iteration_count | The KDF algorithm is repeated several times to| - | | slow down brute force attacks on passwords | - | | (called *N* or CPU/memory cost in scrypt). | - | | The default value for PBKDF2 is 1000. | - | | The default value for scrypt is 16384. | - +------------------+-----------------------------------------------+ - | salt_size | Salt is used to thwart dictionary and rainbow | - | | attacks on passwords. The default value is 8 | - | | bytes. | - +------------------+-----------------------------------------------+ - | block_size | *(scrypt only)* Memory-cost (r). The default | - | | value is 8. | - +------------------+-----------------------------------------------+ - | parallelization | *(scrypt only)* CPU-cost (p). The default | - | | value is 1. | - +------------------+-----------------------------------------------+ + Parameters for the key derivation function (KDF). + Refer to :ref:`the encryption parameters<enc_params>` . key_params (DER object or None): The ``parameters`` field to use in the ``AlgorithmIdentifier`` @@ -103,8 +88,8 @@ def wrap(private_key, key_oid, passphrase=None, protection=None, If not specified, a new RNG will be instantiated from :mod:`Cryptodome.Random`. - Return: - The PKCS#8-wrapped private key (possibly encrypted), as a byte string. + Returns: + bytes: The PKCS#8-wrapped private key (possibly encrypted). """ # @@ -145,8 +130,10 @@ def unwrap(p8_private_key, passphrase=None): """Unwrap a private key from a PKCS#8 blob (clear or encrypted). Args: - p8_private_key (byte string): - The private key wrapped into a PKCS#8 blob, DER encoded. + p8_private_key (bytes): + The private key wrapped into a PKCS#8 container, DER encoded. + + Keyword Args: passphrase (byte string or string): The passphrase to use to decrypt the blob (if it is encrypted). @@ -154,8 +141,8 @@ def unwrap(p8_private_key, passphrase=None): A tuple containing #. the algorithm identifier of the wrapped key (OID, dotted string) - #. the private key (byte string, DER encoded) - #. the associated parameters (byte string, DER encoded) or ``None`` + #. the private key (bytes, DER encoded) + #. the associated parameters (bytes, DER encoded) or ``None`` Raises: ValueError : if decoding fails diff --git a/frozen_deps/Cryptodome/IO/PKCS8.pyi b/frozen_deps/Cryptodome/IO/PKCS8.pyi index be716af..c8d0c10 100644 --- a/frozen_deps/Cryptodome/IO/PKCS8.pyi +++ b/frozen_deps/Cryptodome/IO/PKCS8.pyi @@ -1,14 +1,17 @@ -from typing import Dict, Tuple, Optional, Union, Callable +from typing import Tuple, Optional, Union, Callable +from typing_extensions import NotRequired from Cryptodome.Util.asn1 import DerObject +from Cryptodome.IO._PBES import ProtParams + def wrap(private_key: bytes, key_oid: str, - passphrase: Union[bytes, str] = ..., - protection: str = ..., - prot_params: Dict = ..., - key_params: Optional[DerObject] = ..., - randfunc: Optional[Callable[[int],str]] = ...) -> bytes: ... + passphrase: Union[bytes, str] = ..., + protection: str = ..., + prot_params: Optional[ProtParams] = ..., + key_params: Optional[DerObject] = ..., + randfunc: Optional[Callable[[int], str]] = ...) -> bytes: ... def unwrap(p8_private_key: bytes, passphrase: Optional[Union[bytes, str]] = ...) -> Tuple[str, bytes, Optional[bytes]]: ... 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 diff --git a/frozen_deps/Cryptodome/IO/_PBES.pyi b/frozen_deps/Cryptodome/IO/_PBES.pyi index a8a34ce..0673364 100644 --- a/frozen_deps/Cryptodome/IO/_PBES.pyi +++ b/frozen_deps/Cryptodome/IO/_PBES.pyi @@ -1,4 +1,5 @@ -from typing import Dict, Optional, Callable +from typing import Optional, Callable, TypedDict +from typing_extensions import NotRequired class PbesError(ValueError): ... @@ -7,13 +8,19 @@ class PBES1(object): @staticmethod def decrypt(data: bytes, passphrase: bytes) -> bytes: ... +class ProtParams(TypedDict): + iteration_count: NotRequired[int] + salt_size: NotRequired[int] + block_size: NotRequired[int] + parallelization: NotRequired[int] + class PBES2(object): @staticmethod def encrypt(data: bytes, passphrase: bytes, - protection: str, - prot_params: Optional[Dict] = ..., - randfunc: Optional[Callable[[int],bytes]] = ...) -> bytes: ... + protection: str, + prot_params: Optional[ProtParams] = ..., + randfunc: Optional[Callable[[int],bytes]] = ...) -> bytes: ... @staticmethod def decrypt(data:bytes, passphrase: bytes) -> bytes: ... |