diff options
Diffstat (limited to 'cryptography/hazmat')
23 files changed, 379 insertions, 232 deletions
diff --git a/cryptography/hazmat/backends/commoncrypto/backend.py b/cryptography/hazmat/backends/commoncrypto/backend.py index 4faca73e..213cbd8c 100644 --- a/cryptography/hazmat/backends/commoncrypto/backend.py +++ b/cryptography/hazmat/backends/commoncrypto/backend.py @@ -28,7 +28,7 @@ from cryptography.hazmat.primitives.ciphers.algorithms import ( AES, ARC4, Blowfish, CAST5, TripleDES ) from cryptography.hazmat.primitives.ciphers.modes import ( - CBC, CFB, CTR, ECB, GCM, OFB + CBC, CFB, CFB8, CTR, ECB, GCM, OFB ) @@ -154,7 +154,7 @@ class Backend(object): def _register_cipher_adapter(self, cipher_cls, cipher_const, mode_cls, mode_const): if (cipher_cls, mode_cls) in self._cipher_registry: - raise ValueError("Duplicate registration for: {0} {1}".format( + raise ValueError("Duplicate registration for: {0} {1}.".format( cipher_cls, mode_cls) ) self._cipher_registry[cipher_cls, mode_cls] = (cipher_const, @@ -165,6 +165,7 @@ class Backend(object): (CBC, self._lib.kCCModeCBC), (ECB, self._lib.kCCModeECB), (CFB, self._lib.kCCModeCFB), + (CFB8, self._lib.kCCModeCFB8), (OFB, self._lib.kCCModeOFB), (CTR, self._lib.kCCModeCTR), (GCM, self._lib.kCCModeGCM), @@ -178,6 +179,7 @@ class Backend(object): for mode_cls, mode_const in [ (CBC, self._lib.kCCModeCBC), (CFB, self._lib.kCCModeCFB), + (CFB8, self._lib.kCCModeCFB8), (OFB, self._lib.kCCModeOFB), ]: self._register_cipher_adapter( @@ -226,7 +228,7 @@ class Backend(object): # rdar://15589470 raise ValueError( "The length of the provided data is not a multiple of " - "the block length" + "the block length." ) else: raise InternalError( @@ -264,7 +266,7 @@ class _CipherContext(object): # This bug has been filed as rdar://15589470 self._bytes_processed = 0 if (isinstance(cipher, interfaces.BlockCipherAlgorithm) and not - isinstance(mode, (OFB, CFB, CTR))): + isinstance(mode, (OFB, CFB, CFB8, CTR))): self._byte_block_size = cipher.block_size // 8 else: self._byte_block_size = 1 @@ -275,7 +277,7 @@ class _CipherContext(object): except KeyError: raise UnsupportedAlgorithm( "cipher {0} in {1} mode is not supported " - "by this backend".format( + "by this backend.".format( cipher.name, mode.name if mode else mode), _Reasons.UNSUPPORTED_CIPHER ) @@ -322,7 +324,7 @@ class _CipherContext(object): if self._bytes_processed % self._byte_block_size: raise ValueError( "The length of the provided data is not a multiple of " - "the block length" + "the block length." ) buf = self._backend._ffi.new("unsigned char[]", self._byte_block_size) outlen = self._backend._ffi.new("size_t *") @@ -349,7 +351,7 @@ class _GCMCipherContext(object): except KeyError: raise UnsupportedAlgorithm( "cipher {0} in {1} mode is not supported " - "by this backend".format( + "by this backend.".format( cipher.name, mode.name if mode else mode), _Reasons.UNSUPPORTED_CIPHER ) @@ -423,7 +425,7 @@ class _HashContext(object): methods = self._backend._hash_mapping[self.algorithm.name] except KeyError: raise UnsupportedAlgorithm( - "{0} is not a supported hash on this backend".format( + "{0} is not a supported hash on this backend.".format( algorithm.name), _Reasons.UNSUPPORTED_HASH ) @@ -467,7 +469,7 @@ class _HMACContext(object): alg = self._backend._supported_hmac_algorithms[algorithm.name] except KeyError: raise UnsupportedAlgorithm( - "{0} is not a supported HMAC hash on this backend".format( + "{0} is not a supported HMAC hash on this backend.".format( algorithm.name), _Reasons.UNSUPPORTED_HASH ) diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py index 753f4fc6..21d307cf 100644 --- a/cryptography/hazmat/backends/multibackend.py +++ b/cryptography/hazmat/backends/multibackend.py @@ -52,7 +52,7 @@ class MultiBackend(object): except UnsupportedAlgorithm: pass raise UnsupportedAlgorithm( - "cipher {0} in {1} mode is not supported by this backend".format( + "cipher {0} in {1} mode is not supported by this backend.".format( algorithm.name, mode.name if mode else mode), _Reasons.UNSUPPORTED_CIPHER ) @@ -64,7 +64,7 @@ class MultiBackend(object): except UnsupportedAlgorithm: pass raise UnsupportedAlgorithm( - "cipher {0} in {1} mode is not supported by this backend".format( + "cipher {0} in {1} mode is not supported by this backend.".format( algorithm.name, mode.name if mode else mode), _Reasons.UNSUPPORTED_CIPHER ) @@ -82,7 +82,7 @@ class MultiBackend(object): except UnsupportedAlgorithm: pass raise UnsupportedAlgorithm( - "{0} is not a supported hash on this backend".format( + "{0} is not a supported hash on this backend.".format( algorithm.name), _Reasons.UNSUPPORTED_HASH ) @@ -100,7 +100,7 @@ class MultiBackend(object): except UnsupportedAlgorithm: pass raise UnsupportedAlgorithm( - "{0} is not a supported hash on this backend".format( + "{0} is not a supported hash on this backend.".format( algorithm.name), _Reasons.UNSUPPORTED_HASH ) @@ -121,7 +121,7 @@ class MultiBackend(object): except UnsupportedAlgorithm: pass raise UnsupportedAlgorithm( - "{0} is not a supported hash on this backend".format( + "{0} is not a supported hash on this backend.".format( algorithm.name), _Reasons.UNSUPPORTED_HASH ) @@ -129,13 +129,13 @@ class MultiBackend(object): def generate_rsa_private_key(self, public_exponent, key_size): for b in self._filtered_backends(RSABackend): return b.generate_rsa_private_key(public_exponent, key_size) - raise UnsupportedAlgorithm("RSA is not supported by the backend", + raise UnsupportedAlgorithm("RSA is not supported by the backend.", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) def create_rsa_signature_ctx(self, private_key, padding, algorithm): for b in self._filtered_backends(RSABackend): return b.create_rsa_signature_ctx(private_key, padding, algorithm) - raise UnsupportedAlgorithm("RSA is not supported by the backend", + raise UnsupportedAlgorithm("RSA is not supported by the backend.", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) def create_rsa_verification_ctx(self, public_key, signature, padding, @@ -143,44 +143,62 @@ class MultiBackend(object): for b in self._filtered_backends(RSABackend): return b.create_rsa_verification_ctx(public_key, signature, padding, algorithm) - raise UnsupportedAlgorithm("RSA is not supported by the backend", + raise UnsupportedAlgorithm("RSA is not supported by the backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + + def mgf1_hash_supported(self, algorithm): + for b in self._filtered_backends(RSABackend): + return b.mgf1_hash_supported(algorithm) + raise UnsupportedAlgorithm("RSA is not supported by the backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + + def decrypt_rsa(self, private_key, ciphertext, padding): + for b in self._filtered_backends(RSABackend): + return b.decrypt_rsa(private_key, ciphertext, padding) + raise UnsupportedAlgorithm("RSA is not supported by the backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + + def encrypt_rsa(self, public_key, plaintext, padding): + for b in self._filtered_backends(RSABackend): + return b.encrypt_rsa(public_key, plaintext, padding) + raise UnsupportedAlgorithm("RSA is not supported by the backend.", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) def generate_dsa_parameters(self, key_size): for b in self._filtered_backends(DSABackend): return b.generate_dsa_parameters(key_size) - raise UnsupportedAlgorithm("DSA is not supported by the backend", + raise UnsupportedAlgorithm("DSA is not supported by the backend.", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) def generate_dsa_private_key(self, parameters): for b in self._filtered_backends(DSABackend): return b.generate_dsa_private_key(parameters) - raise UnsupportedAlgorithm("DSA is not supported by the backend", + raise UnsupportedAlgorithm("DSA is not supported by the backend.", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) def create_dsa_verification_ctx(self, public_key, signature, algorithm): for b in self._filtered_backends(DSABackend): return b.create_dsa_verification_ctx(public_key, signature, algorithm) - raise UnsupportedAlgorithm("DSA is not supported by the backend", + raise UnsupportedAlgorithm("DSA is not supported by the backend.", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) def create_dsa_signature_ctx(self, private_key, algorithm): for b in self._filtered_backends(DSABackend): return b.create_dsa_signature_ctx(private_key, algorithm) - raise UnsupportedAlgorithm("DSA is not supported by the backend", + raise UnsupportedAlgorithm("DSA is not supported by the backend.", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) def dsa_hash_supported(self, algorithm): for b in self._filtered_backends(DSABackend): return b.dsa_hash_supported(algorithm) - raise UnsupportedAlgorithm("DSA is not supported by the backend", + raise UnsupportedAlgorithm("DSA is not supported by the backend.", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) def dsa_parameters_supported(self, p, q, g): for b in self._filtered_backends(DSABackend): return b.dsa_parameters_supported(p, q, g) - raise UnsupportedAlgorithm("DSA is not supported by the backend", + raise UnsupportedAlgorithm("DSA is not supported by the backend.", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) def cmac_algorithm_supported(self, algorithm): @@ -195,5 +213,5 @@ class MultiBackend(object): return b.create_cmac_ctx(algorithm) except UnsupportedAlgorithm: pass - raise UnsupportedAlgorithm("This backend does not support CMAC", + raise UnsupportedAlgorithm("This backend does not support CMAC.", _Reasons.UNSUPPORTED_CIPHER) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index c8a93ef6..5d9626d0 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -38,7 +38,7 @@ from cryptography.hazmat.primitives.ciphers.algorithms import ( AES, ARC4, Blowfish, CAST5, Camellia, IDEA, SEED, TripleDES ) from cryptography.hazmat.primitives.ciphers.modes import ( - CBC, CFB, CTR, ECB, GCM, OFB + CBC, CFB, CFB8, CTR, ECB, GCM, OFB ) @@ -114,11 +114,14 @@ class Backend(object): def openssl_version_text(self): """ - Friendly string name of linked OpenSSL. + Friendly string name of the loaded OpenSSL library. This is not + necessarily the same version as it was compiled against. Example: OpenSSL 1.0.1e 11 Feb 2013 """ - return self._ffi.string(self._lib.OPENSSL_VERSION_TEXT).decode("ascii") + return self._ffi.string( + self._lib.SSLeay_version(self._lib.SSLEAY_VERSION) + ).decode("ascii") def create_hmac_ctx(self, key, algorithm): return _HMACContext(self, key, algorithm) @@ -134,6 +137,14 @@ class Backend(object): return _HashContext(self, algorithm) def cipher_supported(self, cipher, mode): + if self._evp_cipher_supported(cipher, mode): + return True + elif isinstance(mode, CTR) and isinstance(cipher, AES): + return True + else: + return False + + def _evp_cipher_supported(self, cipher, mode): try: adapter = self._cipher_registry[type(cipher), type(mode)] except KeyError: @@ -143,22 +154,25 @@ class Backend(object): def register_cipher_adapter(self, cipher_cls, mode_cls, adapter): if (cipher_cls, mode_cls) in self._cipher_registry: - raise ValueError("Duplicate registration for: {0} {1}".format( + raise ValueError("Duplicate registration for: {0} {1}.".format( cipher_cls, mode_cls) ) self._cipher_registry[cipher_cls, mode_cls] = adapter def _register_default_ciphers(self): - for cipher_cls, mode_cls in itertools.product( - [AES, Camellia], - [CBC, CTR, ECB, OFB, CFB], - ): + for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8]: self.register_cipher_adapter( - cipher_cls, + AES, mode_cls, GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}") ) - for mode_cls in [CBC, CFB, OFB]: + for mode_cls in [CBC, CTR, ECB, OFB, CFB]: + self.register_cipher_adapter( + Camellia, + mode_cls, + GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}") + ) + for mode_cls in [CBC, CFB, CFB8, OFB]: self.register_cipher_adapter( TripleDES, mode_cls, @@ -197,10 +211,24 @@ class Backend(object): ) def create_symmetric_encryption_ctx(self, cipher, mode): - return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) + if (isinstance(mode, CTR) and isinstance(cipher, AES) + and not self._evp_cipher_supported(cipher, mode)): + # This is needed to provide support for AES CTR mode in OpenSSL + # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5 + # extended life ends 2020). + return _AESCTRCipherContext(self, cipher, mode) + else: + return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) def create_symmetric_decryption_ctx(self, cipher, mode): - return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) + if (isinstance(mode, CTR) and isinstance(cipher, AES) + and not self._evp_cipher_supported(cipher, mode)): + # This is needed to provide support for AES CTR mode in OpenSSL + # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5 + # extended life ends 2020). + return _AESCTRCipherContext(self, cipher, mode) + else: + return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) def pbkdf2_hmac_supported(self, algorithm): if self._lib.Cryptography_HAS_PBKDF2_HMAC: @@ -233,7 +261,7 @@ class Backend(object): if not isinstance(algorithm, hashes.SHA1): raise UnsupportedAlgorithm( "This version of OpenSSL only supports PBKDF2HMAC with " - "SHA1", + "SHA1.", _Reasons.UNSUPPORTED_HASH ) res = self._lib.PKCS5_PBKDF2_HMAC_SHA1( @@ -271,7 +299,7 @@ class Backend(object): def _unknown_error(self, error): return InternalError( "Unknown error code {0} from OpenSSL, " - "you should probably file a bug. {1}".format( + "you should probably file a bug. {1}.".format( error.code, self._err_string(error.code) ) ) @@ -328,13 +356,13 @@ class Backend(object): def generate_rsa_private_key(self, public_exponent, key_size): if public_exponent < 3: - raise ValueError("public_exponent must be >= 3") + raise ValueError("public_exponent must be >= 3.") if public_exponent & 1 == 0: - raise ValueError("public_exponent must be odd") + raise ValueError("public_exponent must be odd.") if key_size < 512: - raise ValueError("key_size must be at least 512-bits") + raise ValueError("key_size must be at least 512-bits.") ctx = self._lib.RSA_new() assert ctx != self._ffi.NULL @@ -509,13 +537,13 @@ class Backend(object): def generate_dsa_parameters(self, key_size): if key_size not in (1024, 2048, 3072): raise ValueError( - "Key size must be 1024 or 2048 or 3072 bits") + "Key size must be 1024 or 2048 or 3072 bits.") if (self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f and key_size > 1024): raise ValueError( "Key size must be 1024 because OpenSSL < 1.0.0 doesn't " - "support larger key sizes") + "support larger key sizes.") ctx = self._lib.DSA_new() assert ctx != self._ffi.NULL @@ -614,28 +642,28 @@ class Backend(object): padding_enum = self._lib.RSA_PKCS1_OAEP_PADDING if not isinstance(padding._mgf, MGF1): raise UnsupportedAlgorithm( - "Only MGF1 is supported by this backend", + "Only MGF1 is supported by this backend.", _Reasons.UNSUPPORTED_MGF ) if not isinstance(padding._mgf._algorithm, hashes.SHA1): raise UnsupportedAlgorithm( "This backend supports only SHA1 inside MGF1 when " - "using OAEP", + "using OAEP.", _Reasons.UNSUPPORTED_HASH ) if padding._label is not None and padding._label != b"": - raise ValueError("This backend does not support OAEP labels") + raise ValueError("This backend does not support OAEP labels.") if not isinstance(padding._algorithm, hashes.SHA1): raise UnsupportedAlgorithm( - "This backend only supports SHA1 when using OAEP", + "This backend only supports SHA1 when using OAEP.", _Reasons.UNSUPPORTED_HASH ) else: raise UnsupportedAlgorithm( - "{0} is not supported by this backend".format( + "{0} is not supported by this backend.".format( padding.name ), _Reasons.UNSUPPORTED_PADDING @@ -715,14 +743,14 @@ class Backend(object): self._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE) raise ValueError( "Data too long for key size. Encrypt less data or use a " - "larger key size" + "larger key size." ) else: assert ( errors[0].reason == self._lib.RSA_R_BLOCK_TYPE_IS_NOT_01 or errors[0].reason == self._lib.RSA_R_BLOCK_TYPE_IS_NOT_02 ) - raise ValueError("Decryption failed") + raise ValueError("Decryption failed.") def cmac_algorithm_supported(self, algorithm): return ( @@ -838,7 +866,7 @@ class _CipherContext(object): except KeyError: raise UnsupportedAlgorithm( "cipher {0} in {1} mode is not supported " - "by this backend".format( + "by this backend.".format( cipher.name, mode.name if mode else mode), _Reasons.UNSUPPORTED_CIPHER ) @@ -847,7 +875,7 @@ class _CipherContext(object): if evp_cipher == self._backend._ffi.NULL: raise UnsupportedAlgorithm( "cipher {0} in {1} mode is not supported " - "by this backend".format( + "by this backend.".format( cipher.name, mode.name if mode else mode), _Reasons.UNSUPPORTED_CIPHER ) @@ -973,6 +1001,41 @@ class _CipherContext(object): return self._tag +@utils.register_interface(interfaces.CipherContext) +class _AESCTRCipherContext(object): + """ + This is needed to provide support for AES CTR mode in OpenSSL 0.9.8. It can + be removed when we drop 0.9.8 support (RHEL5 extended life ends 2020). + """ + def __init__(self, backend, cipher, mode): + self._backend = backend + + self._key = self._backend._ffi.new("AES_KEY *") + assert self._key != self._backend._ffi.NULL + res = self._backend._lib.AES_set_encrypt_key( + cipher.key, len(cipher.key) * 8, self._key + ) + assert res == 0 + self._ecount = self._backend._ffi.new("char[]", 16) + self._nonce = self._backend._ffi.new("char[16]", mode.nonce) + self._num = self._backend._ffi.new("unsigned int *", 0) + + def update(self, data): + buf = self._backend._ffi.new("unsigned char[]", len(data)) + self._backend._lib.AES_ctr128_encrypt( + data, buf, len(data), self._key, self._nonce, + self._ecount, self._num + ) + return self._backend._ffi.buffer(buf)[:] + + def finalize(self): + self._key = None + self._ecount = None + self._nonce = None + self._num = None + return b"" + + @utils.register_interface(interfaces.HashContext) class _HashContext(object): def __init__(self, backend, algorithm, ctx=None): @@ -988,7 +1051,7 @@ class _HashContext(object): algorithm.name.encode("ascii")) if evp_md == self._backend._ffi.NULL: raise UnsupportedAlgorithm( - "{0} is not a supported hash on this backend".format( + "{0} is not a supported hash on this backend.".format( algorithm.name), _Reasons.UNSUPPORTED_HASH ) @@ -1039,7 +1102,7 @@ class _HMACContext(object): algorithm.name.encode('ascii')) if evp_md == self._backend._ffi.NULL: raise UnsupportedAlgorithm( - "{0} is not a supported hash on this backend".format( + "{0} is not a supported hash on this backend.".format( algorithm.name), _Reasons.UNSUPPORTED_HASH ) @@ -1108,7 +1171,7 @@ class _RSASignatureContext(object): if not isinstance(padding, interfaces.AsymmetricPadding): raise TypeError( - "Expected provider of interfaces.AsymmetricPadding") + "Expected provider of interfaces.AsymmetricPadding.") if isinstance(padding, PKCS1v15): if self._backend._lib.Cryptography_HAS_PKEY_CTX: @@ -1119,7 +1182,7 @@ class _RSASignatureContext(object): elif isinstance(padding, PSS): if not isinstance(padding._mgf, MGF1): raise UnsupportedAlgorithm( - "Only MGF1 is supported by this backend", + "Only MGF1 is supported by this backend.", _Reasons.UNSUPPORTED_MGF ) @@ -1144,7 +1207,7 @@ class _RSASignatureContext(object): self._finalize_method = self._finalize_pss else: raise UnsupportedAlgorithm( - "{0} is not supported by this backend".format(padding.name), + "{0} is not supported by this backend.".format(padding.name), _Reasons.UNSUPPORTED_PADDING ) @@ -1154,13 +1217,13 @@ class _RSASignatureContext(object): def update(self, data): if self._hash_ctx is None: - raise AlreadyFinalized("Context has already been finalized") + raise AlreadyFinalized("Context has already been finalized.") self._hash_ctx.update(data) def finalize(self): if self._hash_ctx is None: - raise AlreadyFinalized("Context has already been finalized") + raise AlreadyFinalized("Context has already been finalized.") evp_pkey = self._backend._rsa_private_key_to_evp_pkey( self._private_key) @@ -1308,7 +1371,7 @@ class _RSAVerificationContext(object): if not isinstance(padding, interfaces.AsymmetricPadding): raise TypeError( - "Expected provider of interfaces.AsymmetricPadding") + "Expected provider of interfaces.AsymmetricPadding.") if isinstance(padding, PKCS1v15): if self._backend._lib.Cryptography_HAS_PKEY_CTX: @@ -1319,7 +1382,7 @@ class _RSAVerificationContext(object): elif isinstance(padding, PSS): if not isinstance(padding._mgf, MGF1): raise UnsupportedAlgorithm( - "Only MGF1 is supported by this backend", + "Only MGF1 is supported by this backend.", _Reasons.UNSUPPORTED_MGF ) @@ -1346,7 +1409,7 @@ class _RSAVerificationContext(object): self._verify_method = self._verify_pss else: raise UnsupportedAlgorithm( - "{0} is not supported by this backend".format(padding.name), + "{0} is not supported by this backend.".format(padding.name), _Reasons.UNSUPPORTED_PADDING ) @@ -1356,13 +1419,13 @@ class _RSAVerificationContext(object): def update(self, data): if self._hash_ctx is None: - raise AlreadyFinalized("Context has already been finalized") + raise AlreadyFinalized("Context has already been finalized.") self._hash_ctx.update(data) def verify(self): if self._hash_ctx is None: - raise AlreadyFinalized("Context has already been finalized") + raise AlreadyFinalized("Context has already been finalized.") evp_pkey = self._backend._rsa_public_key_to_evp_pkey( self._public_key) @@ -1496,13 +1559,13 @@ class _DSAVerificationContext(object): def update(self, data): if self._hash_ctx is None: - raise AlreadyFinalized("Context has already been finalized") + raise AlreadyFinalized("Context has already been finalized.") self._hash_ctx.update(data) def verify(self): if self._hash_ctx is None: - raise AlreadyFinalized("Context has already been finalized") + raise AlreadyFinalized("Context has already been finalized.") self._dsa_cdata = self._backend._dsa_cdata_from_public_key( self._public_key) @@ -1541,13 +1604,13 @@ class _DSASignatureContext(object): def update(self, data): if self._hash_ctx is None: - raise AlreadyFinalized("Context has already been finalized") + raise AlreadyFinalized("Context has already been finalized.") self._hash_ctx.update(data) def finalize(self): if self._hash_ctx is None: - raise AlreadyFinalized("Context has already been finalized") + raise AlreadyFinalized("Context has already been finalized.") data_to_sign = self._hash_ctx.finalize() self._hash_ctx = None @@ -1570,7 +1633,7 @@ class _DSASignatureContext(object): class _CMACContext(object): def __init__(self, backend, algorithm, ctx=None): if not backend.cmac_algorithm_supported(algorithm): - raise UnsupportedAlgorithm("This backend does not support CMAC", + raise UnsupportedAlgorithm("This backend does not support CMAC.", _Reasons.UNSUPPORTED_CIPHER) self._backend = backend diff --git a/cryptography/hazmat/bindings/openssl/aes.py b/cryptography/hazmat/bindings/openssl/aes.py index 17c154cf..b0e00721 100644 --- a/cryptography/hazmat/bindings/openssl/aes.py +++ b/cryptography/hazmat/bindings/openssl/aes.py @@ -29,6 +29,12 @@ typedef struct aes_key_st AES_KEY; FUNCTIONS = """ int AES_set_encrypt_key(const unsigned char *, const int, AES_KEY *); int AES_set_decrypt_key(const unsigned char *, const int, AES_KEY *); +/* The ctr128_encrypt function is only useful in 0.9.8. You should use EVP for + this in 1.0.0+. */ +void AES_ctr128_encrypt(const unsigned char *, unsigned char *, + const unsigned long, const AES_KEY *, + unsigned char[], unsigned char[], unsigned int *); + """ MACROS = """ diff --git a/cryptography/hazmat/bindings/openssl/binding.py b/cryptography/hazmat/bindings/openssl/binding.py index aa0525ff..464081b0 100644 --- a/cryptography/hazmat/bindings/openssl/binding.py +++ b/cryptography/hazmat/bindings/openssl/binding.py @@ -149,7 +149,7 @@ class Binding(object): lock.release() else: raise RuntimeError( - "Unknown lock mode {0}: lock={1}, file={2}, line={3}".format( + "Unknown lock mode {0}: lock={1}, file={2}, line={3}.".format( mode, n, file, line ) ) diff --git a/cryptography/hazmat/bindings/openssl/ec.py b/cryptography/hazmat/bindings/openssl/ec.py index 45c17c2e..26fc8ff0 100644 --- a/cryptography/hazmat/bindings/openssl/ec.py +++ b/cryptography/hazmat/bindings/openssl/ec.py @@ -27,6 +27,8 @@ static const int Cryptography_HAS_EC_1_0_1; static const int Cryptography_HAS_EC_NISTP_64_GCC_128; static const int Cryptography_HAS_EC2M; +static const int OPENSSL_EC_NAMED_CURVE; + typedef ... EC_KEY; typedef ... EC_GROUP; typedef ... EC_POINT; @@ -61,6 +63,8 @@ int EC_GROUP_set_curve_GF2m( int EC_GROUP_get_curve_GF2m( const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *); +int EC_GROUP_get_degree(const EC_GROUP *); + const EC_METHOD *EC_GROUP_method_of(const EC_GROUP *); const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *); int EC_GROUP_get_curve_name(const EC_GROUP *); @@ -198,6 +202,7 @@ int EC_METHOD_get_field_type(const EC_METHOD *); CUSTOMIZATIONS = """ #ifdef OPENSSL_NO_EC static const long Cryptography_HAS_EC = 0; + typedef void EC_KEY; typedef void EC_GROUP; typedef void EC_POINT; @@ -208,6 +213,8 @@ typedef struct { } EC_builtin_curve; typedef long point_conversion_form_t; +static const int OPENSSL_EC_NAMED_CURVE = 0; + void (*EC_KEY_free)(EC_KEY *) = NULL; size_t (*EC_get_builtin_curves)(EC_builtin_curve *, size_t) = NULL; EC_KEY *(*EC_KEY_new_by_curve_name)(int) = NULL; @@ -250,6 +257,8 @@ int (*EC_GROUP_set_curve_GFp)( int (*EC_GROUP_get_curve_GFp)( const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *); +int (*EC_GROUP_get_degree)(const EC_GROUP *) = NULL; + const EC_METHOD *(*EC_GROUP_method_of)(const EC_GROUP *) = NULL; const EC_POINT *(*EC_GROUP_get0_generator)(const EC_GROUP *) = NULL; int (*EC_GROUP_get_curve_name)(const EC_GROUP *) = NULL; @@ -389,6 +398,7 @@ static const long Cryptography_HAS_EC2M = 1; CONDITIONAL_NAMES = { "Cryptography_HAS_EC": [ + "OPENSSL_EC_NAMED_CURVE", "EC_GROUP_new", "EC_GROUP_free", "EC_GROUP_clear_free", @@ -399,6 +409,7 @@ CONDITIONAL_NAMES = { "EC_GROUP_method_of", "EC_GROUP_get0_generator", "EC_GROUP_get_curve_name", + "EC_GROUP_get_degree", "EC_KEY_free", "EC_get_builtin_curves", "EC_KEY_new_by_curve_name", diff --git a/cryptography/hazmat/bindings/openssl/opensslv.py b/cryptography/hazmat/bindings/openssl/opensslv.py index e4aa6212..ef6e057b 100644 --- a/cryptography/hazmat/bindings/openssl/opensslv.py +++ b/cryptography/hazmat/bindings/openssl/opensslv.py @@ -18,6 +18,8 @@ INCLUDES = """ """ TYPES = """ +/* Note that these will be resolved when cryptography is compiled and are NOT + guaranteed to be the version that it actually loads. */ static const int OPENSSL_VERSION_NUMBER; static const char *const OPENSSL_VERSION_TEXT; """ diff --git a/cryptography/hazmat/bindings/openssl/ssl.py b/cryptography/hazmat/bindings/openssl/ssl.py index 0b15411c..94b96d98 100644 --- a/cryptography/hazmat/bindings/openssl/ssl.py +++ b/cryptography/hazmat/bindings/openssl/ssl.py @@ -15,6 +15,8 @@ from __future__ import absolute_import, division, print_function INCLUDES = """ #include <openssl/ssl.h> + +typedef STACK_OF(SSL_CIPHER) Cryptography_STACK_OF_SSL_CIPHER; """ TYPES = """ @@ -24,6 +26,7 @@ TYPES = """ static const long Cryptography_HAS_SSL2; static const long Cryptography_HAS_TLSv1_1; static const long Cryptography_HAS_TLSv1_2; +static const long Cryptography_HAS_SECURE_RENEGOTIATION; /* Internally invented symbol to tell us if SNI is supported */ static const long Cryptography_HAS_TLSEXT_HOSTNAME; @@ -84,6 +87,8 @@ static const long SSL_OP_COOKIE_EXCHANGE; static const long SSL_OP_NO_TICKET; static const long SSL_OP_ALL; static const long SSL_OP_SINGLE_ECDH_USE; +static const long SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; +static const long SSL_OP_LEGACY_SERVER_CONNECT; static const long SSL_VERIFY_PEER; static const long SSL_VERIFY_FAIL_IF_NO_PEER_CERT; static const long SSL_VERIFY_CLIENT_ONCE; @@ -153,6 +158,8 @@ typedef struct { static const long TLSEXT_NAMETYPE_host_name; typedef ... SSL_CIPHER; +typedef ... Cryptography_STACK_OF_SSL_CIPHER; +typedef ... COMP_METHOD; """ FUNCTIONS = """ @@ -190,6 +197,11 @@ int SSL_get_error(const SSL *, int); int SSL_do_handshake(SSL *); int SSL_shutdown(SSL *); const char *SSL_get_cipher_list(const SSL *, int); +Cryptography_STACK_OF_SSL_CIPHER *SSL_get_ciphers(const SSL *); + +const COMP_METHOD *SSL_get_current_compression(SSL *); +const COMP_METHOD *SSL_get_current_expansion(SSL *); +const char *SSL_COMP_get_name(const COMP_METHOD *); /* context */ void SSL_CTX_free(SSL_CTX *); @@ -248,6 +260,7 @@ int SSL_want_read(const SSL *); int SSL_want_write(const SSL *); long SSL_total_renegotiations(SSL *); +long SSL_get_secure_renegotiation_support(SSL *); /* Defined as unsigned long because SSL_OP_ALL is greater than signed 32-bit and Windows defines long as 32-bit. */ @@ -351,9 +364,23 @@ int SSL_select_next_proto(unsigned char **, unsigned char *, const unsigned char *, unsigned int); void SSL_get0_next_proto_negotiated(const SSL *, const unsigned char **, unsigned *); + +int sk_SSL_CIPHER_num(Cryptography_STACK_OF_SSL_CIPHER *); +SSL_CIPHER *sk_SSL_CIPHER_value(Cryptography_STACK_OF_SSL_CIPHER *, int); """ CUSTOMIZATIONS = """ +/** Secure renegotiation is supported in OpenSSL >= 0.9.8m + * But some Linux distributions have back ported some features. + */ +#ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION +static const long Cryptography_HAS_SECURE_RENEGOTIATION = 0; +long (*SSL_get_secure_renegotiation_support)(SSL *) = NULL; +const long SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 0; +const long SSL_OP_LEGACY_SERVER_CONNECT = 0; +#else +static const long Cryptography_HAS_SECURE_RENEGOTIATION = 1; +#endif #ifdef OPENSSL_NO_SSL2 static const long Cryptography_HAS_SSL2 = 0; SSL_METHOD* (*SSLv2_method)(void) = NULL; @@ -551,5 +578,11 @@ CONDITIONAL_NAMES = { "SSL_CTX_set_next_proto_select_cb", "SSL_select_next_proto", "SSL_get0_next_proto_negotiated", - ] + ], + + "Cryptography_HAS_SECURE_RENEGOTIATION": [ + "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION", + "SSL_OP_LEGACY_SERVER_CONNECT", + "SSL_get_secure_renegotiation_support", + ], } diff --git a/cryptography/hazmat/primitives/asymmetric/dsa.py b/cryptography/hazmat/primitives/asymmetric/dsa.py index aa3cdc90..a9ae9ecb 100644 --- a/cryptography/hazmat/primitives/asymmetric/dsa.py +++ b/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -27,7 +27,7 @@ def _check_dsa_parameters(modulus, subgroup_order, generator): not isinstance(subgroup_order, six.integer_types) or not isinstance(generator, six.integer_types) ): - raise TypeError("DSA parameters must be integers") + raise TypeError("DSA parameters must be integers.") if (utils.bit_length(modulus), utils.bit_length(subgroup_order)) not in ( @@ -36,10 +36,10 @@ def _check_dsa_parameters(modulus, subgroup_order, generator): (3072, 256)): raise ValueError("modulus and subgroup_order lengths must be " "one of these pairs (1024, 160) or (2048, 256) " - "or (3072, 256)") + "or (3072, 256).") if generator <= 1 or generator >= modulus: - raise ValueError("generator must be > 1 and < modulus") + raise ValueError("generator must be > 1 and < modulus.") @utils.register_interface(interfaces.DSAParameters) @@ -55,7 +55,7 @@ class DSAParameters(object): def generate(cls, key_size, backend): if not isinstance(backend, DSABackend): raise UnsupportedAlgorithm( - "Backend object does not implement DSABackend", + "Backend object does not implement DSABackend.", _Reasons.BACKEND_MISSING_INTERFACE ) @@ -94,13 +94,13 @@ class DSAPrivateKey(object): not isinstance(x, six.integer_types) or not isinstance(y, six.integer_types) ): - raise TypeError("DSAPrivateKey arguments must be integers") + raise TypeError("DSAPrivateKey arguments must be integers.") if x <= 0 or x >= subgroup_order: - raise ValueError("x must be > 0 and < subgroup_order") + raise ValueError("x must be > 0 and < subgroup_order.") if y != pow(generator, x, modulus): - raise ValueError("y must be equal to (generator ** x % modulus)") + raise ValueError("y must be equal to (generator ** x % modulus).") self._modulus = modulus self._subgroup_order = subgroup_order @@ -112,7 +112,7 @@ class DSAPrivateKey(object): def generate(cls, parameters, backend): if not isinstance(backend, DSABackend): raise UnsupportedAlgorithm( - "Backend object does not implement DSABackend", + "Backend object does not implement DSABackend.", _Reasons.BACKEND_MISSING_INTERFACE ) @@ -121,7 +121,7 @@ class DSAPrivateKey(object): def signer(self, algorithm, backend): if not isinstance(backend, DSABackend): raise UnsupportedAlgorithm( - "Backend object does not implement DSABackend", + "Backend object does not implement DSABackend.", _Reasons.BACKEND_MISSING_INTERFACE ) @@ -153,7 +153,7 @@ class DSAPublicKey(object): def __init__(self, modulus, subgroup_order, generator, y): _check_dsa_parameters(modulus, subgroup_order, generator) if not isinstance(y, six.integer_types): - raise TypeError("y must be an integer") + raise TypeError("y must be an integer.") self._modulus = modulus self._subgroup_order = subgroup_order @@ -163,7 +163,7 @@ class DSAPublicKey(object): def verifier(self, signature, algorithm, backend): if not isinstance(backend, DSABackend): raise UnsupportedAlgorithm( - "Backend object does not implement DSABackend", + "Backend object does not implement DSABackend.", _Reasons.BACKEND_MISSING_INTERFACE ) diff --git a/cryptography/hazmat/primitives/asymmetric/padding.py b/cryptography/hazmat/primitives/asymmetric/padding.py index f7710c49..d44bbda5 100644 --- a/cryptography/hazmat/primitives/asymmetric/padding.py +++ b/cryptography/hazmat/primitives/asymmetric/padding.py @@ -44,13 +44,13 @@ class PSS(object): else: if (not isinstance(salt_length, six.integer_types) and salt_length is not self.MAX_LENGTH): - raise TypeError("salt_length must be an integer") + raise TypeError("salt_length must be an integer.") if salt_length is not self.MAX_LENGTH and salt_length < 0: - raise ValueError("salt_length must be zero or greater") + raise ValueError("salt_length must be zero or greater.") if salt_length is None and self._mgf._salt_length is None: - raise ValueError("You must supply salt_length") + raise ValueError("You must supply salt_length.") self._salt_length = salt_length @@ -86,9 +86,9 @@ class MGF1(object): ) if (not isinstance(salt_length, six.integer_types) and salt_length is not self.MAX_LENGTH): - raise TypeError("salt_length must be an integer") + raise TypeError("salt_length must be an integer.") if salt_length is not self.MAX_LENGTH and salt_length < 0: - raise ValueError("salt_length must be zero or greater") + raise ValueError("salt_length must be zero or greater.") self._salt_length = salt_length diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py index 5d3bb36c..a9f57838 100644 --- a/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -28,16 +28,16 @@ class RSAPublicKey(object): not isinstance(public_exponent, six.integer_types) or not isinstance(modulus, six.integer_types) ): - raise TypeError("RSAPublicKey arguments must be integers") + raise TypeError("RSAPublicKey arguments must be integers.") if modulus < 3: - raise ValueError("modulus must be >= 3") + raise ValueError("modulus must be >= 3.") if public_exponent < 3 or public_exponent >= modulus: - raise ValueError("public_exponent must be >= 3 and < modulus") + raise ValueError("public_exponent must be >= 3 and < modulus.") if public_exponent & 1 == 0: - raise ValueError("public_exponent must be odd") + raise ValueError("public_exponent must be odd.") self._public_exponent = public_exponent self._modulus = modulus @@ -45,7 +45,7 @@ class RSAPublicKey(object): def verifier(self, signature, padding, algorithm, backend): if not isinstance(backend, RSABackend): raise UnsupportedAlgorithm( - "Backend object does not implement RSABackend", + "Backend object does not implement RSABackend.", _Reasons.BACKEND_MISSING_INTERFACE ) @@ -55,7 +55,7 @@ class RSAPublicKey(object): def encrypt(self, plaintext, padding, backend): if not isinstance(backend, RSABackend): raise UnsupportedAlgorithm( - "Backend object does not implement RSABackend", + "Backend object does not implement RSABackend.", _Reasons.BACKEND_MISSING_INTERFACE ) @@ -132,43 +132,43 @@ class RSAPrivateKey(object): not isinstance(public_exponent, six.integer_types) or not isinstance(modulus, six.integer_types) ): - raise TypeError("RSAPrivateKey arguments must be integers") + raise TypeError("RSAPrivateKey arguments must be integers.") if modulus < 3: - raise ValueError("modulus must be >= 3") + raise ValueError("modulus must be >= 3.") if p >= modulus: - raise ValueError("p must be < modulus") + raise ValueError("p must be < modulus.") if q >= modulus: - raise ValueError("q must be < modulus") + raise ValueError("q must be < modulus.") if dmp1 >= modulus: - raise ValueError("dmp1 must be < modulus") + raise ValueError("dmp1 must be < modulus.") if dmq1 >= modulus: - raise ValueError("dmq1 must be < modulus") + raise ValueError("dmq1 must be < modulus.") if iqmp >= modulus: - raise ValueError("iqmp must be < modulus") + raise ValueError("iqmp must be < modulus.") if private_exponent >= modulus: - raise ValueError("private_exponent must be < modulus") + raise ValueError("private_exponent must be < modulus.") if public_exponent < 3 or public_exponent >= modulus: - raise ValueError("public_exponent must be >= 3 and < modulus") + raise ValueError("public_exponent must be >= 3 and < modulus.") if public_exponent & 1 == 0: - raise ValueError("public_exponent must be odd") + raise ValueError("public_exponent must be odd.") if dmp1 & 1 == 0: - raise ValueError("dmp1 must be odd") + raise ValueError("dmp1 must be odd.") if dmq1 & 1 == 0: - raise ValueError("dmq1 must be odd") + raise ValueError("dmq1 must be odd.") if p * q != modulus: - raise ValueError("p*q must equal modulus") + raise ValueError("p*q must equal modulus.") self._p = p self._q = q @@ -183,7 +183,7 @@ class RSAPrivateKey(object): def generate(cls, public_exponent, key_size, backend): if not isinstance(backend, RSABackend): raise UnsupportedAlgorithm( - "Backend object does not implement RSABackend", + "Backend object does not implement RSABackend.", _Reasons.BACKEND_MISSING_INTERFACE ) @@ -192,7 +192,7 @@ class RSAPrivateKey(object): def signer(self, padding, algorithm, backend): if not isinstance(backend, RSABackend): raise UnsupportedAlgorithm( - "Backend object does not implement RSABackend", + "Backend object does not implement RSABackend.", _Reasons.BACKEND_MISSING_INTERFACE ) @@ -201,7 +201,7 @@ class RSAPrivateKey(object): def decrypt(self, ciphertext, padding, backend): if not isinstance(backend, RSABackend): raise UnsupportedAlgorithm( - "Backend object does not implement RSABackend", + "Backend object does not implement RSABackend.", _Reasons.BACKEND_MISSING_INTERFACE ) diff --git a/cryptography/hazmat/primitives/ciphers/algorithms.py b/cryptography/hazmat/primitives/ciphers/algorithms.py index 52daf178..bd8437c2 100644 --- a/cryptography/hazmat/primitives/ciphers/algorithms.py +++ b/cryptography/hazmat/primitives/ciphers/algorithms.py @@ -20,7 +20,7 @@ from cryptography.hazmat.primitives import interfaces def _verify_key_size(algorithm, key): # Verify that the key size matches the expected key size if len(key) * 8 not in algorithm.key_sizes: - raise ValueError("Invalid key size ({0}) for {1}".format( + raise ValueError("Invalid key size ({0}) for {1}.".format( len(key) * 8, algorithm.name )) return key diff --git a/cryptography/hazmat/primitives/ciphers/base.py b/cryptography/hazmat/primitives/ciphers/base.py index 2274e945..e3fe5adc 100644 --- a/cryptography/hazmat/primitives/ciphers/base.py +++ b/cryptography/hazmat/primitives/ciphers/base.py @@ -26,12 +26,14 @@ class Cipher(object): def __init__(self, algorithm, mode, backend): if not isinstance(backend, CipherBackend): raise UnsupportedAlgorithm( - "Backend object does not implement CipherBackend", + "Backend object does not implement CipherBackend.", _Reasons.BACKEND_MISSING_INTERFACE ) if not isinstance(algorithm, interfaces.CipherAlgorithm): - raise TypeError("Expected interface of interfaces.CipherAlgorithm") + raise TypeError( + "Expected interface of interfaces.CipherAlgorithm." + ) if mode is not None: mode.validate_for_algorithm(algorithm) @@ -44,7 +46,7 @@ class Cipher(object): if isinstance(self.mode, interfaces.ModeWithAuthenticationTag): if self.mode.tag is not None: raise ValueError( - "Authentication tag must be None when encrypting" + "Authentication tag must be None when encrypting." ) ctx = self._backend.create_symmetric_encryption_ctx( self.algorithm, self.mode @@ -55,7 +57,7 @@ class Cipher(object): if isinstance(self.mode, interfaces.ModeWithAuthenticationTag): if self.mode.tag is None: raise ValueError( - "Authentication tag must be provided when decrypting" + "Authentication tag must be provided when decrypting." ) ctx = self._backend.create_symmetric_decryption_ctx( self.algorithm, self.mode @@ -79,12 +81,12 @@ class _CipherContext(object): def update(self, data): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") return self._ctx.update(data) def finalize(self): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") data = self._ctx.finalize() self._ctx = None return data @@ -100,13 +102,13 @@ class _AEADCipherContext(object): def update(self, data): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") self._updated = True return self._ctx.update(data) def finalize(self): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") data = self._ctx.finalize() self._tag = self._ctx.tag self._ctx = None @@ -114,9 +116,9 @@ class _AEADCipherContext(object): def authenticate_additional_data(self, data): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") if self._updated: - raise AlreadyUpdated("Update has been called on this context") + raise AlreadyUpdated("Update has been called on this context.") self._ctx.authenticate_additional_data(data) @@ -126,5 +128,5 @@ class _AEADEncryptionContext(_AEADCipherContext): def tag(self): if self._ctx is not None: raise NotYetFinalized("You must finalize encryption before " - "getting the tag") + "getting the tag.") return self._tag diff --git a/cryptography/hazmat/primitives/ciphers/modes.py b/cryptography/hazmat/primitives/ciphers/modes.py index 739f23dd..e70a9db5 100644 --- a/cryptography/hazmat/primitives/ciphers/modes.py +++ b/cryptography/hazmat/primitives/ciphers/modes.py @@ -17,6 +17,13 @@ from cryptography import utils from cryptography.hazmat.primitives import interfaces +def _check_iv_length(mode, algorithm): + if len(mode.initialization_vector) * 8 != algorithm.block_size: + raise ValueError("Invalid IV size ({0}) for {1}.".format( + len(mode.initialization_vector), mode.name + )) + + @utils.register_interface(interfaces.Mode) @utils.register_interface(interfaces.ModeWithInitializationVector) class CBC(object): @@ -25,11 +32,7 @@ class CBC(object): def __init__(self, initialization_vector): self.initialization_vector = initialization_vector - def validate_for_algorithm(self, algorithm): - if len(self.initialization_vector) * 8 != algorithm.block_size: - raise ValueError("Invalid iv size ({0}) for {1}".format( - len(self.initialization_vector), self.name - )) + validate_for_algorithm = _check_iv_length @utils.register_interface(interfaces.Mode) @@ -48,11 +51,7 @@ class OFB(object): def __init__(self, initialization_vector): self.initialization_vector = initialization_vector - def validate_for_algorithm(self, algorithm): - if len(self.initialization_vector) * 8 != algorithm.block_size: - raise ValueError("Invalid iv size ({0}) for {1}".format( - len(self.initialization_vector), self.name - )) + validate_for_algorithm = _check_iv_length @utils.register_interface(interfaces.Mode) @@ -63,11 +62,18 @@ class CFB(object): def __init__(self, initialization_vector): self.initialization_vector = initialization_vector - def validate_for_algorithm(self, algorithm): - if len(self.initialization_vector) * 8 != algorithm.block_size: - raise ValueError("Invalid iv size ({0}) for {1}".format( - len(self.initialization_vector), self.name - )) + validate_for_algorithm = _check_iv_length + + +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithInitializationVector) +class CFB8(object): + name = "CFB8" + + def __init__(self, initialization_vector): + self.initialization_vector = initialization_vector + + validate_for_algorithm = _check_iv_length @utils.register_interface(interfaces.Mode) @@ -80,7 +86,7 @@ class CTR(object): def validate_for_algorithm(self, algorithm): if len(self.nonce) * 8 != algorithm.block_size: - raise ValueError("Invalid nonce size ({0}) for {1}".format( + raise ValueError("Invalid nonce size ({0}) for {1}.".format( len(self.nonce), self.name )) @@ -97,7 +103,7 @@ class GCM(object): # for it if tag is not None and len(tag) < 4: raise ValueError( - "Authentication tag must be 4 bytes or longer" + "Authentication tag must be 4 bytes or longer." ) self.initialization_vector = initialization_vector diff --git a/cryptography/hazmat/primitives/cmac.py b/cryptography/hazmat/primitives/cmac.py index 7e7f65ab..fa463ae0 100644 --- a/cryptography/hazmat/primitives/cmac.py +++ b/cryptography/hazmat/primitives/cmac.py @@ -13,8 +13,6 @@ from __future__ import absolute_import, division, print_function -import six - from cryptography import utils from cryptography.exceptions import ( AlreadyFinalized, InvalidSignature, UnsupportedAlgorithm, _Reasons @@ -28,13 +26,13 @@ class CMAC(object): def __init__(self, algorithm, backend, ctx=None): if not isinstance(backend, CMACBackend): raise UnsupportedAlgorithm( - "Backend object does not implement CMACBackend", + "Backend object does not implement CMACBackend.", _Reasons.BACKEND_MISSING_INTERFACE ) if not isinstance(algorithm, interfaces.BlockCipherAlgorithm): raise TypeError( - "Expected instance of interfaces.BlockCipherAlgorithm" + "Expected instance of interfaces.BlockCipherAlgorithm." ) self._algorithm = algorithm @@ -46,28 +44,28 @@ class CMAC(object): def update(self, data): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") - if isinstance(data, six.text_type): - raise TypeError("Unicode-objects must be encoded before hashing") + raise AlreadyFinalized("Context was already finalized.") + if not isinstance(data, bytes): + raise TypeError("data must be bytes.") self._ctx.update(data) def finalize(self): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") digest = self._ctx.finalize() self._ctx = None return digest def verify(self, signature): - if isinstance(signature, six.text_type): - raise TypeError("Unicode-objects must be encoded before verifying") + if not isinstance(signature, bytes): + raise TypeError("signature must be bytes.") digest = self.finalize() if not constant_time.bytes_eq(digest, signature): raise InvalidSignature("Signature did not match digest.") def copy(self): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") return CMAC( self._algorithm, backend=self._backend, diff --git a/cryptography/hazmat/primitives/constant_time.py b/cryptography/hazmat/primitives/constant_time.py index e0e9aa37..4547da13 100644 --- a/cryptography/hazmat/primitives/constant_time.py +++ b/cryptography/hazmat/primitives/constant_time.py @@ -17,8 +17,6 @@ import sys import cffi -import six - from cryptography.hazmat.bindings.utils import _create_modulename TYPES = """ @@ -57,7 +55,7 @@ _lib = _ffi.verify( def bytes_eq(a, b): - if isinstance(a, six.text_type) or isinstance(b, six.text_type): - raise TypeError("Unicode-objects must be encoded before comparing") + if not isinstance(a, bytes) or not isinstance(b, bytes): + raise TypeError("a and b must be bytes.") return _lib.Cryptography_constant_time_bytes_eq(a, len(a), b, len(b)) == 1 diff --git a/cryptography/hazmat/primitives/hashes.py b/cryptography/hazmat/primitives/hashes.py index 35b677b0..04f7620a 100644 --- a/cryptography/hazmat/primitives/hashes.py +++ b/cryptography/hazmat/primitives/hashes.py @@ -13,8 +13,6 @@ from __future__ import absolute_import, division, print_function -import six - from cryptography import utils from cryptography.exceptions import ( AlreadyFinalized, UnsupportedAlgorithm, _Reasons @@ -28,7 +26,7 @@ class Hash(object): def __init__(self, algorithm, backend, ctx=None): if not isinstance(backend, HashBackend): raise UnsupportedAlgorithm( - "Backend object does not implement HashBackend", + "Backend object does not implement HashBackend.", _Reasons.BACKEND_MISSING_INTERFACE ) @@ -45,21 +43,21 @@ class Hash(object): def update(self, data): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") - if isinstance(data, six.text_type): - raise TypeError("Unicode-objects must be encoded before hashing") + raise AlreadyFinalized("Context was already finalized.") + if not isinstance(data, bytes): + raise TypeError("data must be bytes.") self._ctx.update(data) def copy(self): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") return Hash( self.algorithm, backend=self._backend, ctx=self._ctx.copy() ) def finalize(self): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") digest = self._ctx.finalize() self._ctx = None return digest diff --git a/cryptography/hazmat/primitives/hmac.py b/cryptography/hazmat/primitives/hmac.py index afbb2f75..026ad3b3 100644 --- a/cryptography/hazmat/primitives/hmac.py +++ b/cryptography/hazmat/primitives/hmac.py @@ -13,8 +13,6 @@ from __future__ import absolute_import, division, print_function -import six - from cryptography import utils from cryptography.exceptions import ( AlreadyFinalized, InvalidSignature, UnsupportedAlgorithm, _Reasons @@ -28,7 +26,7 @@ class HMAC(object): def __init__(self, key, algorithm, backend, ctx=None): if not isinstance(backend, HMACBackend): raise UnsupportedAlgorithm( - "Backend object does not implement HMACBackend", + "Backend object does not implement HMACBackend.", _Reasons.BACKEND_MISSING_INTERFACE ) @@ -45,14 +43,14 @@ class HMAC(object): def update(self, msg): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") - if isinstance(msg, six.text_type): - raise TypeError("Unicode-objects must be encoded before hashing") + raise AlreadyFinalized("Context was already finalized.") + if not isinstance(msg, bytes): + raise TypeError("msg must be bytes.") self._ctx.update(msg) def copy(self): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") return HMAC( self._key, self.algorithm, @@ -62,14 +60,14 @@ class HMAC(object): def finalize(self): if self._ctx is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") digest = self._ctx.finalize() self._ctx = None return digest def verify(self, signature): - if isinstance(signature, six.text_type): - raise TypeError("Unicode-objects must be encoded before verifying") + if not isinstance(signature, bytes): + raise TypeError("signature must be bytes.") digest = self.finalize() if not constant_time.bytes_eq(digest, signature): raise InvalidSignature("Signature did not match digest.") diff --git a/cryptography/hazmat/primitives/kdf/hkdf.py b/cryptography/hazmat/primitives/kdf/hkdf.py index 03500aaa..04d02b26 100644 --- a/cryptography/hazmat/primitives/kdf/hkdf.py +++ b/cryptography/hazmat/primitives/kdf/hkdf.py @@ -28,12 +28,53 @@ class HKDF(object): def __init__(self, algorithm, length, salt, info, backend): if not isinstance(backend, HMACBackend): raise UnsupportedAlgorithm( - "Backend object does not implement HMACBackend", + "Backend object does not implement HMACBackend.", _Reasons.BACKEND_MISSING_INTERFACE ) self._algorithm = algorithm + if not isinstance(salt, bytes) and salt is not None: + raise TypeError("salt must be bytes.") + + if salt is None: + salt = b"\x00" * (self._algorithm.digest_size // 8) + + self._salt = salt + + self._backend = backend + + self._hkdf_expand = HKDFExpand(self._algorithm, length, info, backend) + + def _extract(self, key_material): + h = hmac.HMAC(self._salt, self._algorithm, backend=self._backend) + h.update(key_material) + return h.finalize() + + def derive(self, key_material): + if not isinstance(key_material, bytes): + raise TypeError("key_material must be bytes.") + + return self._hkdf_expand.derive(self._extract(key_material)) + + def verify(self, key_material, expected_key): + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey + + +@utils.register_interface(interfaces.KeyDerivationFunction) +class HKDFExpand(object): + def __init__(self, algorithm, length, info, backend): + if not isinstance(backend, HMACBackend): + raise UnsupportedAlgorithm( + "Backend object does not implement HMACBackend.", + _Reasons.BACKEND_MISSING_INTERFACE + ) + + self._algorithm = algorithm + + self._backend = backend + max_length = 255 * (algorithm.digest_size // 8) if length > max_length: @@ -44,32 +85,16 @@ class HKDF(object): self._length = length - if isinstance(salt, six.text_type): - raise TypeError( - "Unicode-objects must be encoded before using them as a salt.") - - if salt is None: - salt = b"\x00" * (self._algorithm.digest_size // 8) - - self._salt = salt - - if isinstance(info, six.text_type): - raise TypeError( - "Unicode-objects must be encoded before using them as info.") + if not isinstance(info, bytes) and info is not None: + raise TypeError("info must be bytes.") if info is None: info = b"" self._info = info - self._backend = backend self._used = False - def _extract(self, key_material): - h = hmac.HMAC(self._salt, self._algorithm, backend=self._backend) - h.update(key_material) - return h.finalize() - def _expand(self, key_material): output = [b""] counter = 1 @@ -85,17 +110,14 @@ class HKDF(object): return b"".join(output)[:self._length] def derive(self, key_material): - if isinstance(key_material, six.text_type): - raise TypeError( - "Unicode-objects must be encoded before using them as key " - "material." - ) + if not isinstance(key_material, bytes): + raise TypeError("key_material must be bytes.") if self._used: raise AlreadyFinalized self._used = True - return self._expand(self._extract(key_material)) + return self._expand(key_material) def verify(self, key_material, expected_key): if not constant_time.bytes_eq(self.derive(key_material), expected_key): diff --git a/cryptography/hazmat/primitives/kdf/pbkdf2.py b/cryptography/hazmat/primitives/kdf/pbkdf2.py index bec35bb2..97b6408c 100644 --- a/cryptography/hazmat/primitives/kdf/pbkdf2.py +++ b/cryptography/hazmat/primitives/kdf/pbkdf2.py @@ -13,8 +13,6 @@ from __future__ import absolute_import, division, print_function -import six - from cryptography import utils from cryptography.exceptions import ( AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons @@ -28,38 +26,32 @@ class PBKDF2HMAC(object): def __init__(self, algorithm, length, salt, iterations, backend): if not isinstance(backend, PBKDF2HMACBackend): raise UnsupportedAlgorithm( - "Backend object does not implement PBKDF2HMACBackend", + "Backend object does not implement PBKDF2HMACBackend.", _Reasons.BACKEND_MISSING_INTERFACE ) if not backend.pbkdf2_hmac_supported(algorithm): raise UnsupportedAlgorithm( - "{0} is not supported for PBKDF2 by this backend".format( + "{0} is not supported for PBKDF2 by this backend.".format( algorithm.name), _Reasons.UNSUPPORTED_HASH ) self._used = False self._algorithm = algorithm self._length = length - if isinstance(salt, six.text_type): - raise TypeError( - "Unicode-objects must be encoded before using them as key " - "material." - ) + if not isinstance(salt, bytes): + raise TypeError("salt must be bytes.") self._salt = salt self._iterations = iterations self._backend = backend def derive(self, key_material): if self._used: - raise AlreadyFinalized("PBKDF2 instances can only be used once") + raise AlreadyFinalized("PBKDF2 instances can only be used once.") self._used = True - if isinstance(key_material, six.text_type): - raise TypeError( - "Unicode-objects must be encoded before using them as key " - "material." - ) + if not isinstance(key_material, bytes): + raise TypeError("key_material must be bytes.") return self._backend.derive_pbkdf2_hmac( self._algorithm, self._length, diff --git a/cryptography/hazmat/primitives/padding.py b/cryptography/hazmat/primitives/padding.py index c1a763b5..74f1ef2e 100644 --- a/cryptography/hazmat/primitives/padding.py +++ b/cryptography/hazmat/primitives/padding.py @@ -79,10 +79,10 @@ _lib = _ffi.verify( class PKCS7(object): def __init__(self, block_size): if not (0 <= block_size < 256): - raise ValueError("block_size must be in range(0, 256)") + raise ValueError("block_size must be in range(0, 256).") if block_size % 8 != 0: - raise ValueError("block_size must be a multiple of 8") + raise ValueError("block_size must be a multiple of 8.") self.block_size = block_size @@ -102,10 +102,10 @@ class _PKCS7PaddingContext(object): def update(self, data): if self._buffer is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") - if isinstance(data, six.text_type): - raise TypeError("Unicode-objects must be encoded before padding") + if not isinstance(data, bytes): + raise TypeError("data must be bytes.") self._buffer += data @@ -118,7 +118,7 @@ class _PKCS7PaddingContext(object): def finalize(self): if self._buffer is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") pad_size = self.block_size // 8 - len(self._buffer) result = self._buffer + six.int2byte(pad_size) * pad_size @@ -135,10 +135,10 @@ class _PKCS7UnpaddingContext(object): def update(self, data): if self._buffer is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") - if isinstance(data, six.text_type): - raise TypeError("Unicode-objects must be encoded before unpadding") + if not isinstance(data, bytes): + raise TypeError("data must be bytes.") self._buffer += data @@ -154,17 +154,17 @@ class _PKCS7UnpaddingContext(object): def finalize(self): if self._buffer is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context was already finalized.") if len(self._buffer) != self.block_size // 8: - raise ValueError("Invalid padding bytes") + raise ValueError("Invalid padding bytes.") valid = _lib.Cryptography_check_pkcs7_padding( self._buffer, self.block_size // 8 ) if not valid: - raise ValueError("Invalid padding bytes") + raise ValueError("Invalid padding bytes.") pad_size = six.indexbytes(self._buffer, -1) res = self._buffer[:-pad_size] diff --git a/cryptography/hazmat/primitives/twofactor/hotp.py b/cryptography/hazmat/primitives/twofactor/hotp.py index 41c467c8..d0b476a7 100644 --- a/cryptography/hazmat/primitives/twofactor/hotp.py +++ b/cryptography/hazmat/primitives/twofactor/hotp.py @@ -29,7 +29,7 @@ class HOTP(object): def __init__(self, key, length, algorithm, backend): if not isinstance(backend, HMACBackend): raise UnsupportedAlgorithm( - "Backend object does not implement HMACBackend", + "Backend object does not implement HMACBackend.", _Reasons.BACKEND_MISSING_INTERFACE ) @@ -37,13 +37,13 @@ class HOTP(object): raise ValueError("Key length has to be at least 128 bits.") if not isinstance(length, six.integer_types): - raise TypeError("Length parameter must be an integer type") + raise TypeError("Length parameter must be an integer type.") if length < 6 or length > 8: raise ValueError("Length of HOTP has to be between 6 to 8.") if not isinstance(algorithm, (SHA1, SHA256, SHA512)): - raise TypeError("Algorithm must be SHA1, SHA256 or SHA512") + raise TypeError("Algorithm must be SHA1, SHA256 or SHA512.") self._key = key self._length = length @@ -57,15 +57,13 @@ class HOTP(object): def verify(self, hotp, counter): if not constant_time.bytes_eq(self.generate(counter), hotp): - raise InvalidToken("Supplied HOTP value does not match") + raise InvalidToken("Supplied HOTP value does not match.") def _dynamic_truncate(self, counter): ctx = hmac.HMAC(self._key, self._algorithm, self._backend) ctx.update(struct.pack(">Q", counter)) hmac_value = ctx.finalize() - offset_bits = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111 - - offset = int(offset_bits) + offset = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111 p = hmac_value[offset:offset + 4] return struct.unpack(">I", p)[0] & 0x7fffffff diff --git a/cryptography/hazmat/primitives/twofactor/totp.py b/cryptography/hazmat/primitives/twofactor/totp.py index e55ba00d..854c5163 100644 --- a/cryptography/hazmat/primitives/twofactor/totp.py +++ b/cryptography/hazmat/primitives/twofactor/totp.py @@ -25,7 +25,7 @@ class TOTP(object): def __init__(self, key, length, algorithm, time_step, backend): if not isinstance(backend, HMACBackend): raise UnsupportedAlgorithm( - "Backend object does not implement HMACBackend", + "Backend object does not implement HMACBackend.", _Reasons.BACKEND_MISSING_INTERFACE ) @@ -38,4 +38,4 @@ class TOTP(object): def verify(self, totp, time): if not constant_time.bytes_eq(self.generate(time), totp): - raise InvalidToken("Supplied TOTP value does not match") + raise InvalidToken("Supplied TOTP value does not match.") |