diff options
Diffstat (limited to 'cryptography/hazmat')
-rw-r--r-- | cryptography/hazmat/bindings/__init__.py | 5 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/interfaces.py | 66 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/backend.py | 365 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/bignum.py | 3 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/engine.py | 10 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/err.py | 14 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/evp.py | 5 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/pem.py | 9 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/rsa.py | 27 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/ssl.py | 189 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/ciphers/algorithms.py | 78 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/ciphers/base.py | 83 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/ciphers/modes.py | 34 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/hashes.py | 48 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/hmac.py | 34 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/interfaces.py | 41 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/padding.py | 8 |
17 files changed, 771 insertions, 248 deletions
diff --git a/cryptography/hazmat/bindings/__init__.py b/cryptography/hazmat/bindings/__init__.py index eb828999..bd158198 100644 --- a/cryptography/hazmat/bindings/__init__.py +++ b/cryptography/hazmat/bindings/__init__.py @@ -14,7 +14,10 @@ from cryptography.hazmat.bindings import openssl -_default_backend = openssl.backend _ALL_BACKENDS = [ openssl.backend ] + + +def default_backend(): + return openssl.backend diff --git a/cryptography/hazmat/bindings/interfaces.py b/cryptography/hazmat/bindings/interfaces.py new file mode 100644 index 00000000..912476bb --- /dev/null +++ b/cryptography/hazmat/bindings/interfaces.py @@ -0,0 +1,66 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import, division, print_function + +import abc + +import six + + +class CipherBackend(six.with_metaclass(abc.ABCMeta)): + @abc.abstractmethod + def cipher_supported(self, cipher, mode): + """ + Return True if the given cipher and mode are supported. + """ + + @abc.abstractmethod + def register_cipher_adapter(self, cipher, mode, adapter): + """ + Register an adapter for a cipher and mode to a backend specific object. + """ + + @abc.abstractmethod + def create_symmetric_encryption_ctx(self, cipher, mode): + """ + Get a CipherContext that can be used for encryption. + """ + + @abc.abstractmethod + def create_symmetric_decryption_ctx(self, cipher, mode): + """ + Get a CipherContext that can be used for decryption. + """ + + +class HashBackend(six.with_metaclass(abc.ABCMeta)): + @abc.abstractmethod + def hash_supported(self, algorithm): + """ + Return True if the hash algorithm is supported by this backend. + """ + + @abc.abstractmethod + def create_hash_ctx(self, algorithm): + """ + Create a HashContext for calculating a message digest. + """ + + +class HMACBackend(six.with_metaclass(abc.ABCMeta)): + @abc.abstractmethod + def create_hmac_ctx(self, key, algorithm): + """ + Create a HashContext for calculating a message authentication code. + """ diff --git a/cryptography/hazmat/bindings/openssl/backend.py b/cryptography/hazmat/bindings/openssl/backend.py index 0c3d22d5..6ab4dc26 100644 --- a/cryptography/hazmat/bindings/openssl/backend.py +++ b/cryptography/hazmat/bindings/openssl/backend.py @@ -18,16 +18,23 @@ import sys import cffi -from cryptography.exceptions import UnsupportedAlgorithm +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, InvalidTag +from cryptography.hazmat.bindings.interfaces import ( + CipherBackend, HashBackend, HMACBackend +) from cryptography.hazmat.primitives import interfaces from cryptography.hazmat.primitives.ciphers.algorithms import ( - AES, Blowfish, Camellia, CAST5, TripleDES, + AES, Blowfish, Camellia, CAST5, TripleDES, ARC4, ) from cryptography.hazmat.primitives.ciphers.modes import ( - CBC, CTR, ECB, OFB, CFB + CBC, CTR, ECB, OFB, CFB, GCM, ) +@utils.register_interface(CipherBackend) +@utils.register_interface(HashBackend) +@utils.register_interface(HMACBackend) class Backend(object): """ OpenSSL API wrapper. @@ -63,9 +70,8 @@ class Backend(object): def __init__(self): self._ensure_ffi_initialized() - self.ciphers = Ciphers(self) - self.hashes = Hashes(self) - self.hmacs = HMACs(self) + self._cipher_registry = {} + self._register_default_ciphers() @classmethod def _ensure_ffi_initialized(cls): @@ -123,6 +129,104 @@ class Backend(object): """ return self.ffi.string(self.lib.OPENSSL_VERSION_TEXT).decode("ascii") + def create_hmac_ctx(self, key, algorithm): + return _HMACContext(self, key, algorithm) + + def hash_supported(self, algorithm): + digest = self.lib.EVP_get_digestbyname(algorithm.name.encode("ascii")) + return digest != self.ffi.NULL + + def create_hash_ctx(self, algorithm): + return _HashContext(self, algorithm) + + def cipher_supported(self, cipher, mode): + try: + adapter = self._cipher_registry[type(cipher), type(mode)] + except KeyError: + return False + evp_cipher = adapter(self, cipher, mode) + return self.ffi.NULL != evp_cipher + + 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( + 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], + ): + self.register_cipher_adapter( + cipher_cls, + mode_cls, + GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}") + ) + for mode_cls in [CBC, CFB, OFB]: + self.register_cipher_adapter( + TripleDES, + mode_cls, + GetCipherByName("des-ede3-{mode.name}") + ) + for mode_cls in [CBC, CFB, OFB, ECB]: + self.register_cipher_adapter( + Blowfish, + mode_cls, + GetCipherByName("bf-{mode.name}") + ) + self.register_cipher_adapter( + CAST5, + ECB, + GetCipherByName("cast5-ecb") + ) + self.register_cipher_adapter( + ARC4, + type(None), + GetCipherByName("rc4") + ) + self.register_cipher_adapter( + AES, + GCM, + GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}") + ) + + def create_symmetric_encryption_ctx(self, cipher, mode): + return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) + + def create_symmetric_decryption_ctx(self, cipher, mode): + return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) + + def _handle_error(self, mode): + code = self.lib.ERR_get_error() + if not code and isinstance(mode, GCM): + raise InvalidTag + assert code != 0 + lib = self.lib.ERR_GET_LIB(code) + func = self.lib.ERR_GET_FUNC(code) + reason = self.lib.ERR_GET_REASON(code) + return self._handle_error_code(lib, func, reason) + + def _handle_error_code(self, lib, func, reason): + if lib == self.lib.ERR_LIB_EVP: + if func == self.lib.EVP_F_EVP_ENCRYPTFINAL_EX: + if reason == self.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: + raise ValueError( + "The length of the provided data is not a multiple of " + "the block length" + ) + elif func == self.lib.EVP_F_EVP_DECRYPTFINAL_EX: + if reason == self.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: + raise ValueError( + "The length of the provided data is not a multiple of " + "the block length" + ) + + raise SystemError( + "Unknown error code from OpenSSL, you should probably file a bug." + ) + class GetCipherByName(object): def __init__(self, fmt): @@ -133,18 +237,24 @@ class GetCipherByName(object): return backend.lib.EVP_get_cipherbyname(cipher_name.encode("ascii")) -@interfaces.register(interfaces.CipherContext) +@utils.register_interface(interfaces.CipherContext) +@utils.register_interface(interfaces.AEADCipherContext) +@utils.register_interface(interfaces.AEADEncryptionContext) class _CipherContext(object): _ENCRYPT = 1 _DECRYPT = 0 def __init__(self, backend, cipher, mode, operation): self._backend = backend + self._cipher = cipher + self._mode = mode + self._operation = operation + self._tag = None ctx = self._backend.lib.EVP_CIPHER_CTX_new() ctx = self._backend.ffi.gc(ctx, self._backend.lib.EVP_CIPHER_CTX_free) - registry = self._backend.ciphers._cipher_registry + registry = self._backend._cipher_registry try: adapter = registry[type(cipher), type(mode)] except KeyError: @@ -172,6 +282,26 @@ class _CipherContext(object): ctx, len(cipher.key) ) assert res != 0 + if isinstance(mode, GCM): + res = self._backend.lib.EVP_CIPHER_CTX_ctrl( + ctx, self._backend.lib.Cryptography_EVP_CTRL_GCM_SET_IVLEN, + len(iv_nonce), self._backend.ffi.NULL + ) + assert res != 0 + if operation == self._DECRYPT: + if not mode.tag: + raise ValueError("Authentication tag must be supplied " + "when decrypting") + res = self._backend.lib.EVP_CIPHER_CTX_ctrl( + ctx, self._backend.lib.Cryptography_EVP_CTRL_GCM_SET_TAG, + len(mode.tag), mode.tag + ) + assert res != 0 + else: + if mode.tag: + raise ValueError("Authentication tag must be None when " + "encrypting") + # pass key/iv res = self._backend.lib.EVP_CipherInit_ex(ctx, self._backend.ffi.NULL, self._backend.ffi.NULL, @@ -185,9 +315,8 @@ class _CipherContext(object): self._ctx = ctx def update(self, data): - block_size = self._backend.lib.EVP_CIPHER_CTX_block_size(self._ctx) buf = self._backend.ffi.new("unsigned char[]", - len(data) + block_size - 1) + len(data) + self._cipher.block_size - 1) outlen = self._backend.ffi.new("int *") res = self._backend.lib.EVP_CipherUpdate(self._ctx, buf, outlen, data, len(data)) @@ -195,156 +324,132 @@ class _CipherContext(object): return self._backend.ffi.buffer(buf)[:outlen[0]] def finalize(self): - block_size = self._backend.lib.EVP_CIPHER_CTX_block_size(self._ctx) - buf = self._backend.ffi.new("unsigned char[]", block_size) + buf = self._backend.ffi.new("unsigned char[]", self._cipher.block_size) outlen = self._backend.ffi.new("int *") res = self._backend.lib.EVP_CipherFinal_ex(self._ctx, buf, outlen) - assert res != 0 + if res == 0: + self._backend._handle_error(self._mode) + + if (isinstance(self._mode, GCM) and + self._operation == self._ENCRYPT): + block_byte_size = self._cipher.block_size // 8 + tag_buf = self._backend.ffi.new("unsigned char[]", block_byte_size) + res = self._backend.lib.EVP_CIPHER_CTX_ctrl( + self._ctx, self._backend.lib.Cryptography_EVP_CTRL_GCM_GET_TAG, + block_byte_size, tag_buf + ) + assert res != 0 + self._tag = self._backend.ffi.buffer(tag_buf)[:] + res = self._backend.lib.EVP_CIPHER_CTX_cleanup(self._ctx) assert res == 1 return self._backend.ffi.buffer(buf)[:outlen[0]] - -class Ciphers(object): - def __init__(self, backend): - super(Ciphers, self).__init__() - self._backend = backend - self._cipher_registry = {} - self._register_default_ciphers() - - def supported(self, cipher, mode): - try: - adapter = self._cipher_registry[type(cipher), type(mode)] - except KeyError: - return False - evp_cipher = adapter(self._backend, cipher, mode) - return self._backend.ffi.NULL != evp_cipher - - 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( - 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], - ): - self.register_cipher_adapter( - cipher_cls, - mode_cls, - GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}") - ) - for mode_cls in [CBC, CFB, OFB]: - self.register_cipher_adapter( - TripleDES, - mode_cls, - GetCipherByName("des-ede3-{mode.name}") - ) - for mode_cls in [CBC, CFB, OFB, ECB]: - self.register_cipher_adapter( - Blowfish, - mode_cls, - GetCipherByName("bf-{mode.name}") - ) - self.register_cipher_adapter( - CAST5, - ECB, - GetCipherByName("cast5-ecb") + def authenticate_additional_data(self, data): + outlen = self._backend.ffi.new("int *") + res = self._backend.lib.EVP_CipherUpdate( + self._ctx, self._backend.ffi.NULL, outlen, data, len(data) ) + assert res != 0 - def create_encrypt_ctx(self, cipher, mode): - return _CipherContext(self._backend, cipher, mode, - _CipherContext._ENCRYPT) + @property + def tag(self): + return self._tag - def create_decrypt_ctx(self, cipher, mode): - return _CipherContext(self._backend, cipher, mode, - _CipherContext._DECRYPT) +@utils.register_interface(interfaces.HashContext) +class _HashContext(object): + def __init__(self, backend, algorithm, ctx=None): + self.algorithm = algorithm -class Hashes(object): - def __init__(self, backend): - super(Hashes, self).__init__() self._backend = backend - def supported(self, hash_cls): - return (self._backend.ffi.NULL != - self._backend.lib.EVP_get_digestbyname( - hash_cls.name.encode("ascii"))) - - def create_ctx(self, hashobject): - ctx = self._backend.lib.EVP_MD_CTX_create() - ctx = self._backend.ffi.gc(ctx, self._backend.lib.EVP_MD_CTX_destroy) - evp_md = self._backend.lib.EVP_get_digestbyname( - hashobject.name.encode("ascii")) - assert evp_md != self._backend.ffi.NULL - res = self._backend.lib.EVP_DigestInit_ex(ctx, evp_md, - self._backend.ffi.NULL) + if ctx is None: + ctx = self._backend.lib.EVP_MD_CTX_create() + ctx = self._backend.ffi.gc(ctx, + self._backend.lib.EVP_MD_CTX_destroy) + evp_md = self._backend.lib.EVP_get_digestbyname( + algorithm.name.encode("ascii")) + assert evp_md != self._backend.ffi.NULL + res = self._backend.lib.EVP_DigestInit_ex(ctx, evp_md, + self._backend.ffi.NULL) + assert res != 0 + + self._ctx = ctx + + def copy(self): + copied_ctx = self._backend.lib.EVP_MD_CTX_create() + copied_ctx = self._backend.ffi.gc(copied_ctx, + self._backend.lib.EVP_MD_CTX_destroy) + res = self._backend.lib.EVP_MD_CTX_copy_ex(copied_ctx, self._ctx) assert res != 0 - return ctx + return _HashContext(self._backend, self.algorithm, ctx=copied_ctx) - def update_ctx(self, ctx, data): - res = self._backend.lib.EVP_DigestUpdate(ctx, data, len(data)) + def update(self, data): + res = self._backend.lib.EVP_DigestUpdate(self._ctx, data, len(data)) assert res != 0 - def finalize_ctx(self, ctx, digest_size): - buf = self._backend.ffi.new("unsigned char[]", digest_size) - res = self._backend.lib.EVP_DigestFinal_ex(ctx, buf, + def finalize(self): + buf = self._backend.ffi.new("unsigned char[]", + self.algorithm.digest_size) + res = self._backend.lib.EVP_DigestFinal_ex(self._ctx, buf, self._backend.ffi.NULL) assert res != 0 - res = self._backend.lib.EVP_MD_CTX_cleanup(ctx) + res = self._backend.lib.EVP_MD_CTX_cleanup(self._ctx) assert res == 1 - return self._backend.ffi.buffer(buf)[:digest_size] - - def copy_ctx(self, ctx): - copied_ctx = self._backend.lib.EVP_MD_CTX_create() - copied_ctx = self._backend.ffi.gc(copied_ctx, - self._backend.lib.EVP_MD_CTX_destroy) - res = self._backend.lib.EVP_MD_CTX_copy_ex(copied_ctx, ctx) - assert res != 0 - return copied_ctx + return self._backend.ffi.buffer(buf)[:] -class HMACs(object): - def __init__(self, backend): - super(HMACs, self).__init__() +@utils.register_interface(interfaces.HashContext) +class _HMACContext(object): + def __init__(self, backend, key, algorithm, ctx=None): + self.algorithm = algorithm self._backend = backend - def create_ctx(self, key, hash_cls): - ctx = self._backend.ffi.new("HMAC_CTX *") - self._backend.lib.HMAC_CTX_init(ctx) - ctx = self._backend.ffi.gc(ctx, self._backend.lib.HMAC_CTX_cleanup) - evp_md = self._backend.lib.EVP_get_digestbyname( - hash_cls.name.encode('ascii')) - assert evp_md != self._backend.ffi.NULL - res = self._backend.lib.Cryptography_HMAC_Init_ex( - ctx, key, len(key), evp_md, self._backend.ffi.NULL - ) - assert res != 0 - return ctx + if ctx is None: + ctx = self._backend.ffi.new("HMAC_CTX *") + self._backend.lib.HMAC_CTX_init(ctx) + ctx = self._backend.ffi.gc(ctx, self._backend.lib.HMAC_CTX_cleanup) + evp_md = self._backend.lib.EVP_get_digestbyname( + algorithm.name.encode('ascii')) + assert evp_md != self._backend.ffi.NULL + res = self._backend.lib.Cryptography_HMAC_Init_ex( + ctx, key, len(key), evp_md, self._backend.ffi.NULL + ) + assert res != 0 + + self._ctx = ctx + self._key = key - def update_ctx(self, ctx, data): - res = self._backend.lib.Cryptography_HMAC_Update(ctx, data, len(data)) + def copy(self): + copied_ctx = self._backend.ffi.new("HMAC_CTX *") + self._backend.lib.HMAC_CTX_init(copied_ctx) + copied_ctx = self._backend.ffi.gc( + copied_ctx, self._backend.lib.HMAC_CTX_cleanup + ) + res = self._backend.lib.Cryptography_HMAC_CTX_copy( + copied_ctx, self._ctx + ) assert res != 0 + return _HMACContext( + self._backend, self._key, self.algorithm, ctx=copied_ctx + ) - def finalize_ctx(self, ctx, digest_size): - buf = self._backend.ffi.new("unsigned char[]", digest_size) - buflen = self._backend.ffi.new("unsigned int *", digest_size) - res = self._backend.lib.Cryptography_HMAC_Final(ctx, buf, buflen) + def update(self, data): + res = self._backend.lib.Cryptography_HMAC_Update( + self._ctx, data, len(data) + ) assert res != 0 - self._backend.lib.HMAC_CTX_cleanup(ctx) - return self._backend.ffi.buffer(buf)[:digest_size] - def copy_ctx(self, ctx): - copied_ctx = self._backend.ffi.new("HMAC_CTX *") - self._backend.lib.HMAC_CTX_init(copied_ctx) - copied_ctx = self._backend.ffi.gc(copied_ctx, - self._backend.lib.HMAC_CTX_cleanup) - res = self._backend.lib.Cryptography_HMAC_CTX_copy(copied_ctx, ctx) + def finalize(self): + buf = self._backend.ffi.new("unsigned char[]", + self.algorithm.digest_size) + buflen = self._backend.ffi.new("unsigned int *", + self.algorithm.digest_size) + res = self._backend.lib.Cryptography_HMAC_Final(self._ctx, buf, buflen) assert res != 0 - return copied_ctx + self._backend.lib.HMAC_CTX_cleanup(self._ctx) + return self._backend.ffi.buffer(buf)[:] backend = Backend() diff --git a/cryptography/hazmat/bindings/openssl/bignum.py b/cryptography/hazmat/bindings/openssl/bignum.py index fcfadff1..1b0fe5ab 100644 --- a/cryptography/hazmat/bindings/openssl/bignum.py +++ b/cryptography/hazmat/bindings/openssl/bignum.py @@ -28,6 +28,9 @@ int BN_set_word(BIGNUM *, BN_ULONG); char *BN_bn2hex(const BIGNUM *); int BN_hex2bn(BIGNUM **, const char *); +int BN_dec2bn(BIGNUM **, const char *); + +int BN_num_bits(const BIGNUM *); """ MACROS = """ diff --git a/cryptography/hazmat/bindings/openssl/engine.py b/cryptography/hazmat/bindings/openssl/engine.py index b76befce..1f377665 100644 --- a/cryptography/hazmat/bindings/openssl/engine.py +++ b/cryptography/hazmat/bindings/openssl/engine.py @@ -36,6 +36,16 @@ void ENGINE_load_builtin_engines(); int ENGINE_ctrl_cmd_string(ENGINE *, const char *, const char *, int); int ENGINE_set_default(ENGINE *, unsigned int); int ENGINE_register_complete(ENGINE *); + +int ENGINE_set_default_RSA(ENGINE *); +int ENGINE_set_default_string(ENGINE *, const char *); +int ENGINE_set_default_DSA(ENGINE *); +int ENGINE_set_default_ECDH(ENGINE *); +int ENGINE_set_default_ECDSA(ENGINE *); +int ENGINE_set_default_DH(ENGINE *); +int ENGINE_set_default_RAND(ENGINE *); +int ENGINE_set_default_ciphers(ENGINE *); +int ENGINE_set_default_digests(ENGINE *); """ MACROS = """ diff --git a/cryptography/hazmat/bindings/openssl/err.py b/cryptography/hazmat/bindings/openssl/err.py index 6a36dee0..f31c2405 100644 --- a/cryptography/hazmat/bindings/openssl/err.py +++ b/cryptography/hazmat/bindings/openssl/err.py @@ -21,6 +21,20 @@ struct ERR_string_data_st { const char *string; }; typedef struct ERR_string_data_st ERR_STRING_DATA; + +static const int ERR_LIB_EVP; +static const int ERR_LIB_PEM; + +static const int EVP_F_EVP_ENCRYPTFINAL_EX; +static const int EVP_F_EVP_DECRYPTFINAL_EX; + +static const int EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH; + +static const int PEM_F_PEM_READ_BIO_PRIVATEKEY; +static const int PEM_F_D2I_PKCS8PRIVATEKEY_BIO; + +static const int PEM_R_BAD_PASSWORD_READ; +static const int ASN1_R_BAD_PASSWORD_READ; """ FUNCTIONS = """ diff --git a/cryptography/hazmat/bindings/openssl/evp.py b/cryptography/hazmat/bindings/openssl/evp.py index da54f89d..8cb44610 100644 --- a/cryptography/hazmat/bindings/openssl/evp.py +++ b/cryptography/hazmat/bindings/openssl/evp.py @@ -16,10 +16,13 @@ INCLUDES = """ """ TYPES = """ +typedef ... EVP_CIPHER; typedef struct { + const EVP_CIPHER *cipher; + ENGINE *engine; + int encrypt; ...; } EVP_CIPHER_CTX; -typedef ... EVP_CIPHER; typedef ... EVP_MD; typedef struct env_md_ctx_st EVP_MD_CTX; diff --git a/cryptography/hazmat/bindings/openssl/pem.py b/cryptography/hazmat/bindings/openssl/pem.py index 00f0dc36..cef7839f 100644 --- a/cryptography/hazmat/bindings/openssl/pem.py +++ b/cryptography/hazmat/bindings/openssl/pem.py @@ -29,6 +29,15 @@ int PEM_write_bio_PrivateKey(BIO *, EVP_PKEY *, const EVP_CIPHER *, EVP_PKEY *PEM_read_bio_PrivateKey(BIO *, EVP_PKEY **, pem_password_cb *, void *); +int PEM_write_bio_PKCS8PrivateKey(BIO *, EVP_PKEY *, const EVP_CIPHER *, + char *, int, pem_password_cb *, void *); + +int i2d_PKCS8PrivateKey_bio(BIO *, EVP_PKEY *, const EVP_CIPHER *, + char *, int, pem_password_cb *, void *); + +EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *, EVP_PKEY **, pem_password_cb *, + void *); + int PEM_write_bio_X509_REQ(BIO *, X509_REQ *); X509_REQ *PEM_read_bio_X509_REQ(BIO *, X509_REQ **, pem_password_cb *, void *); diff --git a/cryptography/hazmat/bindings/openssl/rsa.py b/cryptography/hazmat/bindings/openssl/rsa.py index 21ed5d67..ad0d37b4 100644 --- a/cryptography/hazmat/bindings/openssl/rsa.py +++ b/cryptography/hazmat/bindings/openssl/rsa.py @@ -16,15 +16,40 @@ INCLUDES = """ """ TYPES = """ -typedef ... RSA; +typedef struct rsa_st { + BIGNUM *n; + BIGNUM *e; + BIGNUM *d; + BIGNUM *p; + BIGNUM *q; + BIGNUM *dmp1; + BIGNUM *dmq1; + BIGNUM *iqmp; + ...; +} RSA; typedef ... BN_GENCB; +static const int RSA_PKCS1_PADDING; +static const int RSA_SSLV23_PADDING; +static const int RSA_NO_PADDING; +static const int RSA_PKCS1_OAEP_PADDING; +static const int RSA_X931_PADDING; """ FUNCTIONS = """ RSA *RSA_new(); void RSA_free(RSA *); +int RSA_size(const RSA *); int RSA_generate_key_ex(RSA *, int, BIGNUM *, BN_GENCB *); int RSA_check_key(const RSA *); +RSA *RSAPublicKey_dup(RSA *); +int RSA_public_encrypt(int, const unsigned char *, unsigned char *, + RSA *, int); +int RSA_private_encrypt(int, const unsigned char *, unsigned char *, + RSA *, int); +int RSA_public_decrypt(int, const unsigned char *, unsigned char *, + RSA *, int); +int RSA_private_decrypt(int, const unsigned char *, unsigned char *, + RSA *, int); """ MACROS = """ diff --git a/cryptography/hazmat/bindings/openssl/ssl.py b/cryptography/hazmat/bindings/openssl/ssl.py index 58a64f0b..04611309 100644 --- a/cryptography/hazmat/bindings/openssl/ssl.py +++ b/cryptography/hazmat/bindings/openssl/ssl.py @@ -16,13 +16,200 @@ INCLUDES = """ """ TYPES = """ +static const int SSL_FILETYPE_PEM; +static const int SSL_FILETYPE_ASN1; +static const int SSL_ERROR_NONE; +static const int SSL_ERROR_ZERO_RETURN; +static const int SSL_ERROR_WANT_READ; +static const int SSL_ERROR_WANT_WRITE; +static const int SSL_ERROR_WANT_X509_LOOKUP; +static const int SSL_ERROR_SYSCALL; +static const int SSL_ERROR_SSL; +static const int SSL_SENT_SHUTDOWN; +static const int SSL_RECEIVED_SHUTDOWN; +static const int SSL_OP_NO_SSLv2; +static const int SSL_OP_NO_SSLv3; +static const int SSL_OP_NO_TLSv1; +static const int SSL_OP_SINGLE_DH_USE; +static const int SSL_OP_EPHEMERAL_RSA; +static const int SSL_OP_MICROSOFT_SESS_ID_BUG; +static const int SSL_OP_NETSCAPE_CHALLENGE_BUG; +static const int SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG; +static const int SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG; +static const int SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER; +static const int SSL_OP_MSIE_SSLV2_RSA_PADDING; +static const int SSL_OP_SSLEAY_080_CLIENT_DH_BUG; +static const int SSL_OP_TLS_D5_BUG; +static const int SSL_OP_TLS_BLOCK_PADDING_BUG; +static const int SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; +static const int SSL_OP_CIPHER_SERVER_PREFERENCE; +static const int SSL_OP_TLS_ROLLBACK_BUG; +static const int SSL_OP_PKCS1_CHECK_1; +static const int SSL_OP_PKCS1_CHECK_2; +static const int SSL_OP_NETSCAPE_CA_DN_BUG; +static const int SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG; +static const int SSL_OP_NO_QUERY_MTU; +static const int SSL_OP_COOKIE_EXCHANGE; +static const int SSL_OP_NO_TICKET; +static const int SSL_OP_ALL; +static const int SSL_VERIFY_PEER; +static const int SSL_VERIFY_FAIL_IF_NO_PEER_CERT; +static const int SSL_VERIFY_CLIENT_ONCE; +static const int SSL_VERIFY_NONE; +static const int SSL_SESS_CACHE_OFF; +static const int SSL_SESS_CACHE_CLIENT; +static const int SSL_SESS_CACHE_SERVER; +static const int SSL_SESS_CACHE_BOTH; +static const int SSL_SESS_CACHE_NO_AUTO_CLEAR; +static const int SSL_SESS_CACHE_NO_INTERNAL_LOOKUP; +static const int SSL_SESS_CACHE_NO_INTERNAL_STORE; +static const int SSL_SESS_CACHE_NO_INTERNAL; +static const int SSL_ST_CONNECT; +static const int SSL_ST_ACCEPT; +static const int SSL_ST_MASK; +static const int SSL_ST_INIT; +static const int SSL_ST_BEFORE; +static const int SSL_ST_OK; +static const int SSL_ST_RENEGOTIATE; +static const int SSL_CB_LOOP; +static const int SSL_CB_EXIT; +static const int SSL_CB_READ; +static const int SSL_CB_WRITE; +static const int SSL_CB_ALERT; +static const int SSL_CB_READ_ALERT; +static const int SSL_CB_WRITE_ALERT; +static const int SSL_CB_ACCEPT_LOOP; +static const int SSL_CB_ACCEPT_EXIT; +static const int SSL_CB_CONNECT_LOOP; +static const int SSL_CB_CONNECT_EXIT; +static const int SSL_CB_HANDSHAKE_START; +static const int SSL_CB_HANDSHAKE_DONE; +static const int SSL_MODE_ENABLE_PARTIAL_WRITE; +static const int SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER; +static const int SSL_MODE_AUTO_RETRY; +static const int SSL3_RANDOM_SIZE; +typedef ... X509_STORE_CTX; +static const int X509_V_OK; +typedef ... SSL_METHOD; +typedef ... SSL_CTX; + +typedef struct { + int master_key_length; + unsigned char master_key[...]; + ...; +} SSL_SESSION; + +typedef struct { + unsigned char server_random[...]; + unsigned char client_random[...]; + ...; +} SSL3_STATE; + +typedef struct { + SSL3_STATE *s3; + SSL_SESSION *session; + ...; +} SSL; + +static const int TLSEXT_NAMETYPE_host_name; """ FUNCTIONS = """ void SSL_load_error_strings(); + +int SSL_library_init(); + +/* SSL */ +SSL_CTX *SSL_set_SSL_CTX(SSL *, SSL_CTX *); +SSL_SESSION *SSL_get1_session(SSL *); +int SSL_set_session(SSL *, SSL_SESSION *); +int SSL_get_verify_mode(const SSL *); +void SSL_set_verify_depth(SSL *, int); +int SSL_get_verify_depth(const SSL *); +SSL *SSL_new(SSL_CTX *); +void SSL_free(SSL *); +int SSL_set_fd(SSL *, int); +void SSL_set_bio(SSL *, BIO *, BIO *); +void SSL_set_connect_state(SSL *); +void SSL_set_accept_state(SSL *); +void SSL_set_shutdown(SSL *, int); +int SSL_get_shutdown(const SSL *); +int SSL_pending(const SSL *); +int SSL_write(SSL *, const void *, int); +int SSL_read(SSL *, void *, int); +X509 *SSL_get_peer_certificate(const SSL *); +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); + +/* context */ +void SSL_CTX_free(SSL_CTX *); +long SSL_CTX_set_timeout(SSL_CTX *, long); +int SSL_CTX_set_default_verify_paths(SSL_CTX *); +void SSL_CTX_set_verify_depth(SSL_CTX *, int); +int SSL_CTX_get_verify_mode(const SSL_CTX *); +int SSL_CTX_get_verify_depth(const SSL_CTX *); +int SSL_CTX_set_cipher_list(SSL_CTX *, const char *); +int SSL_CTX_load_verify_locations(SSL_CTX *, const char *, const char *); +void SSL_CTX_set_default_passwd_cb(SSL_CTX *, pem_password_cb *); +void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *, void *); +int SSL_CTX_use_certificate(SSL_CTX *, X509 *); +int SSL_CTX_use_certificate_file(SSL_CTX *, const char *, int); +int SSL_CTX_use_certificate_chain_file(SSL_CTX *, const char *); +int SSL_CTX_use_PrivateKey(SSL_CTX *, EVP_PKEY *); +int SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int); +void SSL_CTX_set_cert_store(SSL_CTX *, X509_STORE *); +X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *); +int SSL_CTX_add_client_CA(SSL_CTX *, X509 *); + +/* X509_STORE_CTX */ +int X509_STORE_CTX_get_error(X509_STORE_CTX *); +void X509_STORE_CTX_set_error(X509_STORE_CTX *, int); +int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *); +X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *); + +/* SSL_SESSION */ +void SSL_SESSION_free(SSL_SESSION *); """ -MACROS = """ +MACROS = MACROS = """ +long SSL_set_mode(SSL *, long); +long SSL_get_mode(SSL *); + +long SSL_set_options(SSL *, long); +long SSL_get_options(SSL *); + +int SSL_want_read(const SSL *); +int SSL_want_write(const SSL *); + +int SSL_total_renegotiations(const SSL *); + +long SSL_CTX_set_options(SSL_CTX *, long); +long SSL_CTX_get_options(SSL_CTX *); +long SSL_CTX_set_mode(SSL_CTX *, long); +long SSL_CTX_get_mode(SSL_CTX *); +long SSL_CTX_set_session_cache_mode(SSL_CTX *, long); +long SSL_CTX_get_session_cache_mode(SSL_CTX *); +long SSL_CTX_set_tmp_dh(SSL_CTX *, DH *); +long SSL_CTX_add_extra_chain_cert(SSL_CTX *, X509 *); + +/*- These aren't macros these functions are all const X on openssl > 1.0.x -*/ + +/* methods */ +const SSL_METHOD *SSLv3_method(); +const SSL_METHOD *SSLv3_server_method(); +const SSL_METHOD *SSLv3_client_method(); +const SSL_METHOD *TLSv1_method(); +const SSL_METHOD *TLSv1_server_method(); +const SSL_METHOD *TLSv1_client_method(); +const SSL_METHOD *SSLv23_method(); +const SSL_METHOD *SSLv23_server_method(); +const SSL_METHOD *SSLv23_client_method(); + +/*- These aren't macros these arguments are all const X on openssl > 1.0.x -*/ +SSL_CTX *SSL_CTX_new(const SSL_METHOD *); +long SSL_CTX_get_timeout(const SSL_CTX *); """ CUSTOMIZATIONS = """ diff --git a/cryptography/hazmat/primitives/ciphers/algorithms.py b/cryptography/hazmat/primitives/ciphers/algorithms.py index 8046bd26..a206b273 100644 --- a/cryptography/hazmat/primitives/ciphers/algorithms.py +++ b/cryptography/hazmat/primitives/ciphers/algorithms.py @@ -13,105 +13,101 @@ from __future__ import absolute_import, division, print_function +from cryptography import utils +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( + len(key) * 8, algorithm.name + )) + return key + + +@utils.register_interface(interfaces.CipherAlgorithm) class AES(object): name = "AES" block_size = 128 key_sizes = frozenset([128, 192, 256]) def __init__(self, key): - super(AES, self).__init__() - self.key = key - - # Verify that the key size matches the expected key size - if self.key_size not in self.key_sizes: - raise ValueError("Invalid key size ({0}) for {1}".format( - self.key_size, self.name - )) + self.key = _verify_key_size(self, key) @property def key_size(self): return len(self.key) * 8 +@utils.register_interface(interfaces.CipherAlgorithm) class Camellia(object): name = "camellia" block_size = 128 key_sizes = frozenset([128, 192, 256]) def __init__(self, key): - super(Camellia, self).__init__() - self.key = key - - # Verify that the key size matches the expected key size - if self.key_size not in self.key_sizes: - raise ValueError("Invalid key size ({0}) for {1}".format( - self.key_size, self.name - )) + self.key = _verify_key_size(self, key) @property def key_size(self): return len(self.key) * 8 +@utils.register_interface(interfaces.CipherAlgorithm) class TripleDES(object): name = "3DES" block_size = 64 key_sizes = frozenset([64, 128, 192]) def __init__(self, key): - super(TripleDES, self).__init__() if len(key) == 8: key += key + key elif len(key) == 16: key += key[:8] - self.key = key - - # Verify that the key size matches the expected key size - if self.key_size not in self.key_sizes: - raise ValueError("Invalid key size ({0}) for {1}".format( - self.key_size, self.name - )) + self.key = _verify_key_size(self, key) @property def key_size(self): return len(self.key) * 8 +@utils.register_interface(interfaces.CipherAlgorithm) class Blowfish(object): name = "Blowfish" block_size = 64 key_sizes = frozenset(range(32, 449, 8)) def __init__(self, key): - super(Blowfish, self).__init__() - self.key = key - - # Verify that the key size matches the expected key size - if self.key_size not in self.key_sizes: - raise ValueError("Invalid key size ({0}) for {1}".format( - self.key_size, self.name - )) + self.key = _verify_key_size(self, key) @property def key_size(self): return len(self.key) * 8 +@utils.register_interface(interfaces.CipherAlgorithm) class CAST5(object): name = "CAST5" block_size = 64 - key_sizes = frozenset([40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128]) + key_sizes = frozenset(range(40, 129, 8)) + + def __init__(self, key): + self.key = _verify_key_size(self, key) + + @property + def key_size(self): + return len(self.key) * 8 + + +@utils.register_interface(interfaces.CipherAlgorithm) +class ARC4(object): + name = "RC4" + block_size = 1 + key_sizes = frozenset([40, 56, 64, 80, 128, 192, 256]) def __init__(self, key): - super(CAST5, self).__init__() - self.key = key - - # Verify that the key size matches the expected key size - if self.key_size not in self.key_sizes: - raise ValueError("Invalid key size ({0}) for {1}".format( - self.key_size, self.name - )) + self.key = _verify_key_size(self, key) @property def key_size(self): diff --git a/cryptography/hazmat/primitives/ciphers/base.py b/cryptography/hazmat/primitives/ciphers/base.py index 1599308c..b8615cb9 100644 --- a/cryptography/hazmat/primitives/ciphers/base.py +++ b/cryptography/hazmat/primitives/ciphers/base.py @@ -13,46 +13,97 @@ from __future__ import absolute_import, division, print_function +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, NotYetFinalized, AlreadyUpdated, +) from cryptography.hazmat.primitives import interfaces class Cipher(object): - def __init__(self, algorithm, mode, backend=None): - super(Cipher, self).__init__() - - if backend is None: - from cryptography.hazmat.bindings import ( - _default_backend as backend, - ) + def __init__(self, algorithm, mode, backend): + if not isinstance(algorithm, interfaces.CipherAlgorithm): + raise TypeError("Expected interface of interfaces.CipherAlgorithm") self.algorithm = algorithm self.mode = mode self._backend = backend def encryptor(self): - return _CipherContext( - self._backend.ciphers.create_encrypt_ctx(self.algorithm, - self.mode)) + ctx = self._backend.create_symmetric_encryption_ctx( + self.algorithm, self.mode + ) + return self._wrap_ctx(ctx, True) def decryptor(self): - return _CipherContext( - self._backend.ciphers.create_decrypt_ctx(self.algorithm, - self.mode)) + ctx = self._backend.create_symmetric_decryption_ctx( + self.algorithm, self.mode + ) + return self._wrap_ctx(ctx, False) + + def _wrap_ctx(self, ctx, encrypt): + if isinstance(self.mode, interfaces.ModeWithAuthenticationTag): + if encrypt: + return _AEADEncryptionContext(ctx) + else: + return _AEADCipherContext(ctx) + else: + return _CipherContext(ctx) -@interfaces.register(interfaces.CipherContext) +@utils.register_interface(interfaces.CipherContext) class _CipherContext(object): def __init__(self, ctx): self._ctx = ctx def update(self, data): if self._ctx is None: - raise ValueError("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") + data = self._ctx.finalize() + self._ctx = None + return data + + +@utils.register_interface(interfaces.AEADCipherContext) +@utils.register_interface(interfaces.CipherContext) +class _AEADCipherContext(object): + def __init__(self, ctx): + self._ctx = ctx + self._tag = None + self._updated = False + + def update(self, data): + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized") + self._updated = True return self._ctx.update(data) def finalize(self): if self._ctx is None: - raise ValueError("Context was already finalized") + raise AlreadyFinalized("Context was already finalized") data = self._ctx.finalize() + self._tag = self._ctx.tag self._ctx = None return data + + def authenticate_additional_data(self, data): + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized") + if self._updated: + raise AlreadyUpdated("Update has been called on this context") + self._ctx.authenticate_additional_data(data) + + +@utils.register_interface(interfaces.AEADEncryptionContext) +class _AEADEncryptionContext(_AEADCipherContext): + @property + def tag(self): + if self._ctx is not None: + raise NotYetFinalized("You must finalize encryption before " + "getting the tag") + return self._tag diff --git a/cryptography/hazmat/primitives/ciphers/modes.py b/cryptography/hazmat/primitives/ciphers/modes.py index e54872a6..e1c70185 100644 --- a/cryptography/hazmat/primitives/ciphers/modes.py +++ b/cryptography/hazmat/primitives/ciphers/modes.py @@ -13,49 +13,57 @@ from __future__ import absolute_import, division, print_function +from cryptography import utils from cryptography.hazmat.primitives import interfaces -@interfaces.register(interfaces.Mode) -@interfaces.register(interfaces.ModeWithInitializationVector) +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithInitializationVector) class CBC(object): name = "CBC" def __init__(self, initialization_vector): - super(CBC, self).__init__() self.initialization_vector = initialization_vector -@interfaces.register(interfaces.Mode) +@utils.register_interface(interfaces.Mode) class ECB(object): name = "ECB" -@interfaces.register(interfaces.Mode) -@interfaces.register(interfaces.ModeWithInitializationVector) +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithInitializationVector) class OFB(object): name = "OFB" def __init__(self, initialization_vector): - super(OFB, self).__init__() self.initialization_vector = initialization_vector -@interfaces.register(interfaces.Mode) -@interfaces.register(interfaces.ModeWithInitializationVector) +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithInitializationVector) class CFB(object): name = "CFB" def __init__(self, initialization_vector): - super(CFB, self).__init__() self.initialization_vector = initialization_vector -@interfaces.register(interfaces.Mode) -@interfaces.register(interfaces.ModeWithNonce) +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithNonce) class CTR(object): name = "CTR" def __init__(self, nonce): - super(CTR, self).__init__() self.nonce = nonce + + +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithInitializationVector) +@utils.register_interface(interfaces.ModeWithAuthenticationTag) +class GCM(object): + name = "GCM" + + def __init__(self, initialization_vector, tag=None): + self.initialization_vector = initialization_vector + self.tag = tag diff --git a/cryptography/hazmat/primitives/hashes.py b/cryptography/hazmat/primitives/hashes.py index bdad5e16..bee188b3 100644 --- a/cryptography/hazmat/primitives/hashes.py +++ b/cryptography/hazmat/primitives/hashes.py @@ -15,91 +15,97 @@ from __future__ import absolute_import, division, print_function import six +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized from cryptography.hazmat.primitives import interfaces -@interfaces.register(interfaces.HashContext) +@utils.register_interface(interfaces.HashContext) class Hash(object): - def __init__(self, algorithm, backend=None, ctx=None): + def __init__(self, algorithm, backend, ctx=None): if not isinstance(algorithm, interfaces.HashAlgorithm): raise TypeError("Expected instance of interfaces.HashAlgorithm.") self.algorithm = algorithm - if backend is None: - from cryptography.hazmat.bindings import _default_backend - backend = _default_backend - self._backend = backend if ctx is None: - self._ctx = self._backend.hashes.create_ctx(self.algorithm) + self._ctx = self._backend.create_hash_ctx(self.algorithm) else: - self._ctx = None + self._ctx = ctx 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") - self._backend.hashes.update_ctx(self._ctx, data) + self._ctx.update(data) def copy(self): - return self.__class__(self.algorithm, backend=self._backend, - ctx=self._backend.hashes.copy_ctx(self._ctx)) + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized") + return Hash( + self.algorithm, backend=self._backend, ctx=self._ctx.copy() + ) def finalize(self): - return self._backend.hashes.finalize_ctx(self._ctx, - self.algorithm.digest_size) + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized") + digest = self._ctx.finalize() + self._ctx = None + return digest -@interfaces.register(interfaces.HashAlgorithm) +@utils.register_interface(interfaces.HashAlgorithm) class SHA1(object): name = "sha1" digest_size = 20 block_size = 64 -@interfaces.register(interfaces.HashAlgorithm) +@utils.register_interface(interfaces.HashAlgorithm) class SHA224(object): name = "sha224" digest_size = 28 block_size = 64 -@interfaces.register(interfaces.HashAlgorithm) +@utils.register_interface(interfaces.HashAlgorithm) class SHA256(object): name = "sha256" digest_size = 32 block_size = 64 -@interfaces.register(interfaces.HashAlgorithm) +@utils.register_interface(interfaces.HashAlgorithm) class SHA384(object): name = "sha384" digest_size = 48 block_size = 128 -@interfaces.register(interfaces.HashAlgorithm) +@utils.register_interface(interfaces.HashAlgorithm) class SHA512(object): name = "sha512" digest_size = 64 block_size = 128 -@interfaces.register(interfaces.HashAlgorithm) +@utils.register_interface(interfaces.HashAlgorithm) class RIPEMD160(object): name = "ripemd160" digest_size = 20 block_size = 64 -@interfaces.register(interfaces.HashAlgorithm) +@utils.register_interface(interfaces.HashAlgorithm) class Whirlpool(object): name = "whirlpool" digest_size = 64 block_size = 64 -@interfaces.register(interfaces.HashAlgorithm) +@utils.register_interface(interfaces.HashAlgorithm) class MD5(object): name = "md5" digest_size = 16 diff --git a/cryptography/hazmat/primitives/hmac.py b/cryptography/hazmat/primitives/hmac.py index 1457ed78..618bccc5 100644 --- a/cryptography/hazmat/primitives/hmac.py +++ b/cryptography/hazmat/primitives/hmac.py @@ -15,37 +15,45 @@ from __future__ import absolute_import, division, print_function import six +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized from cryptography.hazmat.primitives import interfaces -@interfaces.register(interfaces.HashContext) +@utils.register_interface(interfaces.HashContext) class HMAC(object): - def __init__(self, key, algorithm, ctx=None, backend=None): - super(HMAC, self).__init__() + def __init__(self, key, algorithm, backend, ctx=None): if not isinstance(algorithm, interfaces.HashAlgorithm): raise TypeError("Expected instance of interfaces.HashAlgorithm.") self.algorithm = algorithm - if backend is None: - from cryptography.hazmat.bindings import _default_backend - backend = _default_backend - self._backend = backend self._key = key if ctx is None: - self._ctx = self._backend.hmacs.create_ctx(key, self.algorithm) + self._ctx = self._backend.create_hmac_ctx(key, self.algorithm) else: self._ctx = ctx 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") - self._backend.hmacs.update_ctx(self._ctx, msg) + self._ctx.update(msg) def copy(self): - return self.__class__(self._key, self.algorithm, backend=self._backend, - ctx=self._backend.hmacs.copy_ctx(self._ctx)) + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized") + return HMAC( + self._key, + self.algorithm, + backend=self._backend, + ctx=self._ctx.copy() + ) def finalize(self): - return self._backend.hmacs.finalize_ctx(self._ctx, - self.algorithm.digest_size) + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized") + digest = self._ctx.finalize() + self._ctx = None + return digest diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py index 67dbe6fa..e3f4f586 100644 --- a/cryptography/hazmat/primitives/interfaces.py +++ b/cryptography/hazmat/primitives/interfaces.py @@ -18,11 +18,18 @@ import abc import six -def register(iface): - def register_decorator(klass): - iface.register(klass) - return klass - return register_decorator +class CipherAlgorithm(six.with_metaclass(abc.ABCMeta)): + @abc.abstractproperty + def name(self): + """ + A string naming this mode. (e.g. AES, Camellia) + """ + + @abc.abstractproperty + def key_size(self): + """ + The size of the key being used as an integer in bits. (e.g. 128, 256) + """ class Mode(six.with_metaclass(abc.ABCMeta)): @@ -49,6 +56,14 @@ class ModeWithNonce(six.with_metaclass(abc.ABCMeta)): """ +class ModeWithAuthenticationTag(six.with_metaclass(abc.ABCMeta)): + @abc.abstractproperty + def tag(self): + """ + The value of the tag supplied to the constructor of this mode. + """ + + class CipherContext(six.with_metaclass(abc.ABCMeta)): @abc.abstractmethod def update(self, data): @@ -63,6 +78,22 @@ class CipherContext(six.with_metaclass(abc.ABCMeta)): """ +class AEADCipherContext(six.with_metaclass(abc.ABCMeta)): + @abc.abstractmethod + def authenticate_additional_data(self, data): + """ + authenticate_additional_data takes bytes and returns nothing. + """ + + +class AEADEncryptionContext(six.with_metaclass(abc.ABCMeta)): + @abc.abstractproperty + def tag(self): + """ + Returns tag bytes after finalizing encryption. + """ + + class PaddingContext(six.with_metaclass(abc.ABCMeta)): @abc.abstractmethod def update(self, data): diff --git a/cryptography/hazmat/primitives/padding.py b/cryptography/hazmat/primitives/padding.py index 6849d149..cfa90db9 100644 --- a/cryptography/hazmat/primitives/padding.py +++ b/cryptography/hazmat/primitives/padding.py @@ -15,6 +15,7 @@ import cffi import six +from cryptography import utils from cryptography.hazmat.primitives import interfaces @@ -64,7 +65,6 @@ bool Cryptography_check_pkcs7_padding(const uint8_t *data, uint8_t block_len) { class PKCS7(object): def __init__(self, block_size): - super(PKCS7, self).__init__() if not (0 <= block_size < 256): raise ValueError("block_size must be in range(0, 256)") @@ -80,10 +80,9 @@ class PKCS7(object): return _PKCS7UnpaddingContext(self.block_size) -@interfaces.register(interfaces.PaddingContext) +@utils.register_interface(interfaces.PaddingContext) class _PKCS7PaddingContext(object): def __init__(self, block_size): - super(_PKCS7PaddingContext, self).__init__() self.block_size = block_size # TODO: O(n ** 2) complexity for repeated concatentation, we should use # zero-buffer (#193) @@ -115,10 +114,9 @@ class _PKCS7PaddingContext(object): return result -@interfaces.register(interfaces.PaddingContext) +@utils.register_interface(interfaces.PaddingContext) class _PKCS7UnpaddingContext(object): def __init__(self, block_size): - super(_PKCS7UnpaddingContext, self).__init__() self.block_size = block_size # TODO: O(n ** 2) complexity for repeated concatentation, we should use # zero-buffer (#193) |