diff options
-rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 200 | ||||
-rw-r--r-- | cryptography/hazmat/backends/openssl/cmac.py | 80 | ||||
-rw-r--r-- | cryptography/hazmat/backends/openssl/hashes.py | 69 | ||||
-rw-r--r-- | cryptography/hazmat/backends/openssl/hmac.py | 80 | ||||
-rw-r--r-- | cryptography/hazmat/backends/openssl/rsa.py | 12 | ||||
-rw-r--r-- | docs/development/getting-started.rst | 11 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/ec.rst | 2 | ||||
-rw-r--r-- | docs/hazmat/primitives/symmetric-encryption.rst | 6 | ||||
-rw-r--r-- | docs/limitations.rst | 8 |
9 files changed, 273 insertions, 195 deletions
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 8d167ab3..b3396ea9 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -31,6 +31,7 @@ from cryptography.hazmat.backends.interfaces import ( from cryptography.hazmat.backends.openssl.ciphers import ( _AESCTRCipherContext, _CipherContext ) +from cryptography.hazmat.backends.openssl.cmac import _CMACContext from cryptography.hazmat.backends.openssl.dsa import ( _DSAParameters, _DSAPrivateKey, _DSAPublicKey, _DSASignatureContext, _DSAVerificationContext @@ -39,12 +40,14 @@ from cryptography.hazmat.backends.openssl.ec import ( _ECDSASignatureContext, _ECDSAVerificationContext, _EllipticCurvePrivateKey, _EllipticCurvePublicKey ) +from cryptography.hazmat.backends.openssl.hashes import _HashContext +from cryptography.hazmat.backends.openssl.hmac import _HMACContext from cryptography.hazmat.backends.openssl.rsa import ( _RSAPrivateKey, _RSAPublicKey, _RSASignatureContext, _RSAVerificationContext ) from cryptography.hazmat.bindings.openssl.binding import Binding -from cryptography.hazmat.primitives import hashes, interfaces +from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa from cryptography.hazmat.primitives.asymmetric.padding import ( MGF1, OAEP, PKCS1v15, PSS @@ -1026,19 +1029,25 @@ class Backend(object): Generate a new private key on the named curve. """ - curve_nid = self._elliptic_curve_to_nid(curve) + if backend.elliptic_curve_supported(curve): + curve_nid = self._elliptic_curve_to_nid(curve) - ctx = self._lib.EC_KEY_new_by_curve_name(curve_nid) - assert ctx != self._ffi.NULL - ctx = self._ffi.gc(ctx, self._lib.EC_KEY_free) + ctx = self._lib.EC_KEY_new_by_curve_name(curve_nid) + assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.EC_KEY_free) - res = self._lib.EC_KEY_generate_key(ctx) - assert res == 1 + res = self._lib.EC_KEY_generate_key(ctx) + assert res == 1 - res = self._lib.EC_KEY_check_key(ctx) - assert res == 1 + res = self._lib.EC_KEY_check_key(ctx) + assert res == 1 - return _EllipticCurvePrivateKey(self, ctx, curve) + return _EllipticCurvePrivateKey(self, ctx, curve) + else: + raise UnsupportedAlgorithm( + "Backend object does not support {0}.".format(curve.name), + _Reasons.UNSUPPORTED_ELLIPTIC_CURVE + ) def elliptic_curve_private_key_from_numbers(self, numbers): ec_key = self._ec_key_cdata_from_private_numbers(numbers) @@ -1204,175 +1213,4 @@ class GetCipherByName(object): return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii")) -@utils.register_interface(interfaces.HashContext) -class _HashContext(object): - def __init__(self, backend, algorithm, ctx=None): - self.algorithm = algorithm - - self._backend = backend - - 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")) - if evp_md == self._backend._ffi.NULL: - raise UnsupportedAlgorithm( - "{0} is not a supported hash on this backend.".format( - algorithm.name), - _Reasons.UNSUPPORTED_HASH - ) - 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 _HashContext(self._backend, self.algorithm, ctx=copied_ctx) - - def update(self, data): - res = self._backend._lib.EVP_DigestUpdate(self._ctx, data, len(data)) - assert res != 0 - - def finalize(self): - buf = self._backend._ffi.new("unsigned char[]", - self._backend._lib.EVP_MAX_MD_SIZE) - outlen = self._backend._ffi.new("unsigned int *") - res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen) - assert res != 0 - assert outlen[0] == self.algorithm.digest_size - res = self._backend._lib.EVP_MD_CTX_cleanup(self._ctx) - assert res == 1 - return self._backend._ffi.buffer(buf)[:outlen[0]] - - -@utils.register_interface(interfaces.HashContext) -class _HMACContext(object): - def __init__(self, backend, key, algorithm, ctx=None): - self.algorithm = algorithm - self._backend = backend - - 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')) - if evp_md == self._backend._ffi.NULL: - raise UnsupportedAlgorithm( - "{0} is not a supported hash on this backend.".format( - algorithm.name), - _Reasons.UNSUPPORTED_HASH - ) - 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 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 update(self, data): - res = self._backend._lib.Cryptography_HMAC_Update( - self._ctx, data, len(data) - ) - assert res != 0 - - def finalize(self): - buf = self._backend._ffi.new("unsigned char[]", - self._backend._lib.EVP_MAX_MD_SIZE) - outlen = self._backend._ffi.new("unsigned int *") - res = self._backend._lib.Cryptography_HMAC_Final( - self._ctx, buf, outlen - ) - assert res != 0 - assert outlen[0] == self.algorithm.digest_size - self._backend._lib.HMAC_CTX_cleanup(self._ctx) - return self._backend._ffi.buffer(buf)[:outlen[0]] - - -@utils.register_interface(interfaces.CMACContext) -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.", - _Reasons.UNSUPPORTED_CIPHER) - - self._backend = backend - self._key = algorithm.key - self._algorithm = algorithm - self._output_length = algorithm.block_size // 8 - - if ctx is None: - registry = self._backend._cipher_registry - adapter = registry[type(algorithm), CBC] - - evp_cipher = adapter(self._backend, algorithm, CBC) - - ctx = self._backend._lib.CMAC_CTX_new() - - assert ctx != self._backend._ffi.NULL - ctx = self._backend._ffi.gc(ctx, self._backend._lib.CMAC_CTX_free) - - self._backend._lib.CMAC_Init( - ctx, self._key, len(self._key), - evp_cipher, self._backend._ffi.NULL - ) - - self._ctx = ctx - - def update(self, data): - res = self._backend._lib.CMAC_Update(self._ctx, data, len(data)) - assert res == 1 - - def finalize(self): - buf = self._backend._ffi.new("unsigned char[]", self._output_length) - length = self._backend._ffi.new("size_t *", self._output_length) - res = self._backend._lib.CMAC_Final( - self._ctx, buf, length - ) - assert res == 1 - - self._ctx = None - - return self._backend._ffi.buffer(buf)[:] - - def copy(self): - copied_ctx = self._backend._lib.CMAC_CTX_new() - copied_ctx = self._backend._ffi.gc( - copied_ctx, self._backend._lib.CMAC_CTX_free - ) - res = self._backend._lib.CMAC_CTX_copy( - copied_ctx, self._ctx - ) - assert res == 1 - return _CMACContext( - self._backend, self._algorithm, ctx=copied_ctx - ) - - backend = Backend() diff --git a/cryptography/hazmat/backends/openssl/cmac.py b/cryptography/hazmat/backends/openssl/cmac.py new file mode 100644 index 00000000..7acf4391 --- /dev/null +++ b/cryptography/hazmat/backends/openssl/cmac.py @@ -0,0 +1,80 @@ +# 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 + + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import interfaces +from cryptography.hazmat.primitives.ciphers.modes import CBC + + +@utils.register_interface(interfaces.CMACContext) +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.", + _Reasons.UNSUPPORTED_CIPHER) + + self._backend = backend + self._key = algorithm.key + self._algorithm = algorithm + self._output_length = algorithm.block_size // 8 + + if ctx is None: + registry = self._backend._cipher_registry + adapter = registry[type(algorithm), CBC] + + evp_cipher = adapter(self._backend, algorithm, CBC) + + ctx = self._backend._lib.CMAC_CTX_new() + + assert ctx != self._backend._ffi.NULL + ctx = self._backend._ffi.gc(ctx, self._backend._lib.CMAC_CTX_free) + + self._backend._lib.CMAC_Init( + ctx, self._key, len(self._key), + evp_cipher, self._backend._ffi.NULL + ) + + self._ctx = ctx + + def update(self, data): + res = self._backend._lib.CMAC_Update(self._ctx, data, len(data)) + assert res == 1 + + def finalize(self): + buf = self._backend._ffi.new("unsigned char[]", self._output_length) + length = self._backend._ffi.new("size_t *", self._output_length) + res = self._backend._lib.CMAC_Final( + self._ctx, buf, length + ) + assert res == 1 + + self._ctx = None + + return self._backend._ffi.buffer(buf)[:] + + def copy(self): + copied_ctx = self._backend._lib.CMAC_CTX_new() + copied_ctx = self._backend._ffi.gc( + copied_ctx, self._backend._lib.CMAC_CTX_free + ) + res = self._backend._lib.CMAC_CTX_copy( + copied_ctx, self._ctx + ) + assert res == 1 + return _CMACContext( + self._backend, self._algorithm, ctx=copied_ctx + ) diff --git a/cryptography/hazmat/backends/openssl/hashes.py b/cryptography/hazmat/backends/openssl/hashes.py new file mode 100644 index 00000000..da91eef6 --- /dev/null +++ b/cryptography/hazmat/backends/openssl/hashes.py @@ -0,0 +1,69 @@ +# 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 + + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import interfaces + + +@utils.register_interface(interfaces.HashContext) +class _HashContext(object): + def __init__(self, backend, algorithm, ctx=None): + self.algorithm = algorithm + + self._backend = backend + + 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")) + if evp_md == self._backend._ffi.NULL: + raise UnsupportedAlgorithm( + "{0} is not a supported hash on this backend.".format( + algorithm.name), + _Reasons.UNSUPPORTED_HASH + ) + 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 _HashContext(self._backend, self.algorithm, ctx=copied_ctx) + + def update(self, data): + res = self._backend._lib.EVP_DigestUpdate(self._ctx, data, len(data)) + assert res != 0 + + def finalize(self): + buf = self._backend._ffi.new("unsigned char[]", + self._backend._lib.EVP_MAX_MD_SIZE) + outlen = self._backend._ffi.new("unsigned int *") + res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen) + assert res != 0 + assert outlen[0] == self.algorithm.digest_size + res = self._backend._lib.EVP_MD_CTX_cleanup(self._ctx) + assert res == 1 + return self._backend._ffi.buffer(buf)[:outlen[0]] diff --git a/cryptography/hazmat/backends/openssl/hmac.py b/cryptography/hazmat/backends/openssl/hmac.py new file mode 100644 index 00000000..3f1576f5 --- /dev/null +++ b/cryptography/hazmat/backends/openssl/hmac.py @@ -0,0 +1,80 @@ +# 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 + + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import interfaces + + +@utils.register_interface(interfaces.HashContext) +class _HMACContext(object): + def __init__(self, backend, key, algorithm, ctx=None): + self.algorithm = algorithm + self._backend = backend + + 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')) + if evp_md == self._backend._ffi.NULL: + raise UnsupportedAlgorithm( + "{0} is not a supported hash on this backend.".format( + algorithm.name), + _Reasons.UNSUPPORTED_HASH + ) + 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 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 update(self, data): + res = self._backend._lib.Cryptography_HMAC_Update( + self._ctx, data, len(data) + ) + assert res != 0 + + def finalize(self): + buf = self._backend._ffi.new("unsigned char[]", + self._backend._lib.EVP_MAX_MD_SIZE) + outlen = self._backend._ffi.new("unsigned int *") + res = self._backend._lib.Cryptography_HMAC_Final( + self._ctx, buf, outlen + ) + assert res != 0 + assert outlen[0] == self.algorithm.digest_size + self._backend._lib.HMAC_CTX_cleanup(self._ctx) + return self._backend._ffi.buffer(buf)[:outlen[0]] diff --git a/cryptography/hazmat/backends/openssl/rsa.py b/cryptography/hazmat/backends/openssl/rsa.py index 2fada1b9..a62a89f4 100644 --- a/cryptography/hazmat/backends/openssl/rsa.py +++ b/cryptography/hazmat/backends/openssl/rsa.py @@ -422,7 +422,11 @@ class _RSAPrivateKey(object): assert res == 1 self._evp_pkey = evp_pkey - self.key_size = self._backend._lib.BN_num_bits(self._rsa_cdata.n) + self._key_size = self._backend._lib.BN_num_bits(self._rsa_cdata.n) + + @property + def key_size(self): + return self._key_size def signer(self, padding, algorithm): return _RSASignatureContext(self._backend, self, padding, algorithm) @@ -474,7 +478,11 @@ class _RSAPublicKey(object): assert res == 1 self._evp_pkey = evp_pkey - self.key_size = self._backend._lib.BN_num_bits(self._rsa_cdata.n) + self._key_size = self._backend._lib.BN_num_bits(self._rsa_cdata.n) + + @property + def key_size(self): + return self._key_size def verifier(self, signature, padding, algorithm): return _RSAVerificationContext( diff --git a/docs/development/getting-started.rst b/docs/development/getting-started.rst index 4337b47c..f5d6c190 100644 --- a/docs/development/getting-started.rst +++ b/docs/development/getting-started.rst @@ -2,10 +2,10 @@ Getting started =============== Working on ``cryptography`` requires the installation of a small number of -development dependencies in addition to the dependencies for :doc:`/installation`. -These are listed in ``dev-requirements.txt`` and they can be installed in a -`virtualenv`_ using `pip`_. Once you've installed the dependencies, install -``cryptography`` in ``editable`` mode. For example: +development dependencies in addition to the dependencies for +:doc:`/installation`. These are listed in ``dev-requirements.txt`` and they can +be installed in a `virtualenv`_ using `pip`_. Once you've installed the +dependencies, install ``cryptography`` in ``editable`` mode. For example: .. code-block:: console @@ -13,6 +13,9 @@ These are listed in ``dev-requirements.txt`` and they can be installed in a $ pip install --requirement dev-requirements.txt $ pip install --editable . +You will also need to install ``enchant`` using your system's package manager +to check spelling in the documentation. + You are now ready to run the tests and build the documentation. Running tests diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst index 5dc7e2f0..4b3c460e 100644 --- a/docs/hazmat/primitives/asymmetric/ec.rst +++ b/docs/hazmat/primitives/asymmetric/ec.rst @@ -116,7 +116,7 @@ Elliptic Curve Signature Algorithms >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import ec >>> private_key = ec.generate_private_key( - ... ec.SECT283K1(), default_backend() + ... ec.SECP384R1(), default_backend() ... ) >>> signer = private_key.signer(ec.ECDSA(hashes.SHA256())) >>> signer.update(b"this is some data I'd like") diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index bcb1fb35..abc2b076 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -430,9 +430,9 @@ Interfaces make a message the correct size. ``CipherContext`` will not automatically apply any padding; you'll need to add your own. For block ciphers the recommended padding is - :class:`cryptography.hazmat.primitives.padding.PKCS7`. If you are using a + :class:`~cryptography.hazmat.primitives.padding.PKCS7`. If you are using a stream cipher mode (such as - :class:`cryptography.hazmat.primitives.modes.CTR`) you don't have to worry + :class:`~cryptography.hazmat.primitives.modes.CTR`) you don't have to worry about this. .. method:: update(data) @@ -443,7 +443,7 @@ Interfaces When the ``Cipher`` was constructed in a mode that turns it into a stream cipher (e.g. - :class:`cryptography.hazmat.primitives.ciphers.modes.CTR`), this will + :class:`~cryptography.hazmat.primitives.ciphers.modes.CTR`), this will return bytes immediately, however in other modes it will return chunks whose size is determined by the cipher's block size. diff --git a/docs/limitations.rst b/docs/limitations.rst index 5b63ef54..ce61d893 100644 --- a/docs/limitations.rst +++ b/docs/limitations.rst @@ -10,10 +10,10 @@ has some kind of local user access or because of how other software uses uninitialized memory. Python exposes no API for us to implement this reliably and as such almost all -software in Python is potentially vulnerable to this attack. However the -`CERT secure coding guidelines`_ consider this issue as "low severity, -unlikely, expensive to repair" and we do not consider this a high risk for most -users. +software in Python is potentially vulnerable to this attack. The +`CERT secure coding guidelines`_ assesses this issue as "Severity: medium, +Likelihood: unlikely, Remediation Cost: expensive to repair" and we do not +consider this a high risk for most users. .. _`Memory wiping`: http://blogs.msdn.com/b/oldnewthing/archive/2013/05/29/10421912.aspx .. _`CERT secure coding guidelines`: https://www.securecoding.cert.org/confluence/display/seccode/MEM03-C.+Clear+sensitive+information+stored+in+reusable+resources |