diff options
| author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2014-01-27 21:04:03 -0600 |
|---|---|---|
| committer | Paul Kehrer <paul.l.kehrer@gmail.com> | 2014-01-28 11:18:26 -0600 |
| commit | 1050ddf44f0713a587cd0ba239e23c95064a39bc (patch) | |
| tree | 336c1329b82370fda3050b8c787ed8a85d32dc1b /cryptography | |
| parent | 1f8cd620cfbb854b0dfcdbf89c140160a8caba13 (diff) | |
| download | cryptography-1050ddf44f0713a587cd0ba239e23c95064a39bc.tar.gz cryptography-1050ddf44f0713a587cd0ba239e23c95064a39bc.tar.bz2 cryptography-1050ddf44f0713a587cd0ba239e23c95064a39bc.zip | |
PBKDF2 support for OpenSSL backend
Diffstat (limited to 'cryptography')
| -rw-r--r-- | cryptography/hazmat/backends/interfaces.py | 15 | ||||
| -rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 46 | ||||
| -rw-r--r-- | cryptography/hazmat/primitives/kdf/__init__.py | 0 | ||||
| -rw-r--r-- | cryptography/hazmat/primitives/kdf/pbkdf2.py | 46 |
4 files changed, 105 insertions, 2 deletions
diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py index 4fbb3488..936520eb 100644 --- a/cryptography/hazmat/backends/interfaces.py +++ b/cryptography/hazmat/backends/interfaces.py @@ -65,3 +65,18 @@ class HMACBackend(six.with_metaclass(abc.ABCMeta)): """ Create a HashContext for calculating a message authentication code. """ + + +class PBKDF2Backend(six.with_metaclass(abc.ABCMeta)): + @abc.abstractmethod + def pbkdf2_hash_supported(self, algorithm): + """ + Return True if the hash algorithm is supported for PBKDF2 by this + backend. + """ + + @abc.abstractmethod + def derive_pbkdf2(self, algorithm, length, salt, iterations, key_material): + """ + Return length bytes derived from provided PBKDF2 parameters. + """ diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index d8d4669a..ca7d1778 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -20,9 +20,9 @@ from cryptography.exceptions import ( UnsupportedAlgorithm, InvalidTag, InternalError ) from cryptography.hazmat.backends.interfaces import ( - CipherBackend, HashBackend, HMACBackend + CipherBackend, HashBackend, HMACBackend, PBKDF2Backend ) -from cryptography.hazmat.primitives import interfaces +from cryptography.hazmat.primitives import interfaces, hashes from cryptography.hazmat.primitives.ciphers.algorithms import ( AES, Blowfish, Camellia, TripleDES, ARC4, ) @@ -35,6 +35,7 @@ from cryptography.hazmat.bindings.openssl.binding import Binding @utils.register_interface(CipherBackend) @utils.register_interface(HashBackend) @utils.register_interface(HMACBackend) +@utils.register_interface(PBKDF2Backend) class Backend(object): """ OpenSSL API binding interfaces. @@ -133,6 +134,47 @@ class Backend(object): def create_symmetric_decryption_ctx(self, cipher, mode): return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) + def pbkdf2_hash_supported(self, algorithm): + if self._lib.Cryptography_HAS_PBKDF2_HMAC: + digest = self._lib.EVP_get_digestbyname( + algorithm.name.encode("ascii")) + return digest != self._ffi.NULL + else: + return isinstance(algorithm, hashes.SHA1) + + def derive_pbkdf2(self, algorithm, length, salt, iterations, key_material): + buf = self._ffi.new("char[]", length) + if self._lib.Cryptography_HAS_PBKDF2_HMAC: + evp_md = self._lib.EVP_get_digestbyname( + algorithm.name.encode("ascii")) + assert evp_md != self._ffi.NULL + res = self._lib.PKCS5_PBKDF2_HMAC( + key_material, + len(key_material), + salt, + len(salt), + iterations, + evp_md, + length, + buf + ) + assert res == 1 + else: + # OpenSSL < 1.0.0 + assert isinstance(algorithm, hashes.SHA1) + res = self._lib.PKCS5_PBKDF2_HMAC_SHA1( + key_material, + len(key_material), + salt, + len(salt), + iterations, + length, + buf + ) + assert res == 1 + + return self._ffi.buffer(buf)[:] + def _handle_error(self, mode): code = self._lib.ERR_get_error() if not code and isinstance(mode, GCM): diff --git a/cryptography/hazmat/primitives/kdf/__init__.py b/cryptography/hazmat/primitives/kdf/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/cryptography/hazmat/primitives/kdf/__init__.py diff --git a/cryptography/hazmat/primitives/kdf/pbkdf2.py b/cryptography/hazmat/primitives/kdf/pbkdf2.py new file mode 100644 index 00000000..cc01246f --- /dev/null +++ b/cryptography/hazmat/primitives/kdf/pbkdf2.py @@ -0,0 +1,46 @@ +# 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.exceptions import InvalidKey, UnsupportedAlgorithm +from cryptography.hazmat.primitives import constant_time + + +class PBKDF2(object): + def __init__(self, algorithm, length, salt, iterations, backend): + if not backend.pbkdf2_hash_supported(algorithm): + raise UnsupportedAlgorithm( + "{0} is not supported by this backend".format(algorithm.name) + ) + self.algorithm = algorithm + if length > 2**31 - 1: + raise ValueError("Requested length too large.") + self._length = length + # TODO: handle salt + self._salt = salt + self.iterations = iterations + self._backend = backend + + def derive(self, key_material): + return self._backend.derive_pbkdf2( + self.algorithm, + self._length, + self._salt, + self.iterations, + key_material + ) + + def verify(self, key_material, expected_key): + if not constant_time.bytes_eq(key_material, expected_key): + raise InvalidKey("Signature did not match digest.") |
