diff options
| author | David Reid <dreid@dreid.org> | 2014-01-29 15:09:49 -0800 |
|---|---|---|
| committer | David Reid <dreid@dreid.org> | 2014-01-29 15:09:49 -0800 |
| commit | b2ff87737ca27a171ce0034e100841782d19dd7b (patch) | |
| tree | fde20b858f07d70a1dcc3f8f2b78bc02480630e5 /cryptography | |
| parent | babc4d50f42edc1e16c414e4008114d882b4c999 (diff) | |
| parent | 0b181182aef574c436a92a175937af32e54a2378 (diff) | |
| download | cryptography-b2ff87737ca27a171ce0034e100841782d19dd7b.tar.gz cryptography-b2ff87737ca27a171ce0034e100841782d19dd7b.tar.bz2 cryptography-b2ff87737ca27a171ce0034e100841782d19dd7b.zip | |
Merge pull request #520 from reaperhulk/pbkdf2-openssl
PBKDF2 Support (OpenSSL backend)
Diffstat (limited to 'cryptography')
| -rw-r--r-- | cryptography/hazmat/backends/interfaces.py | 16 | ||||
| -rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 48 | ||||
| -rw-r--r-- | cryptography/hazmat/primitives/kdf/__init__.py | 0 | ||||
| -rw-r--r-- | cryptography/hazmat/primitives/kdf/pbkdf2.py | 66 |
4 files changed, 128 insertions, 2 deletions
diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py index 4fbb3488..53c75181 100644 --- a/cryptography/hazmat/backends/interfaces.py +++ b/cryptography/hazmat/backends/interfaces.py @@ -65,3 +65,19 @@ class HMACBackend(six.with_metaclass(abc.ABCMeta)): """ Create a HashContext for calculating a message authentication code. """ + + +class PBKDF2HMACBackend(six.with_metaclass(abc.ABCMeta)): + @abc.abstractmethod + def pbkdf2_hmac_supported(self, algorithm): + """ + Return True if the hash algorithm is supported for PBKDF2 by this + backend. + """ + + @abc.abstractmethod + def derive_pbkdf2_hmac(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..cf931dab 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, PBKDF2HMACBackend ) -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(PBKDF2HMACBackend) class Backend(object): """ OpenSSL API binding interfaces. @@ -133,6 +134,49 @@ class Backend(object): def create_symmetric_decryption_ctx(self, cipher, mode): return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) + def pbkdf2_hmac_supported(self, algorithm): + if self._lib.Cryptography_HAS_PBKDF2_HMAC: + return self.hmac_supported(algorithm) + else: + # OpenSSL < 1.0.0 has an explicit PBKDF2-HMAC-SHA1 function, + # so if the PBKDF2_HMAC function is missing we only support + # SHA1 via PBKDF2_HMAC_SHA1. + return isinstance(algorithm, hashes.SHA1) + + def derive_pbkdf2_hmac(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..71b88211 --- /dev/null +++ b/cryptography/hazmat/primitives/kdf/pbkdf2.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 six + +from cryptography import utils +from cryptography.exceptions import ( + InvalidKey, UnsupportedAlgorithm, AlreadyFinalized +) +from cryptography.hazmat.primitives import constant_time, interfaces + + +@utils.register_interface(interfaces.KeyDerivationFunction) +class PBKDF2HMAC(object): + def __init__(self, algorithm, length, salt, iterations, backend): + if not backend.pbkdf2_hmac_supported(algorithm): + raise UnsupportedAlgorithm( + "{0} is not supported for PBKDF2 by this backend".format( + algorithm.name) + ) + 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." + ) + 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") + self._used = True + + if isinstance(key_material, six.text_type): + raise TypeError( + "Unicode-objects must be encoded before using them as key " + "material." + ) + return self._backend.derive_pbkdf2_hmac( + self._algorithm, + self._length, + self._salt, + self._iterations, + key_material + ) + + def verify(self, key_material, expected_key): + derived_key = self.derive(key_material) + if not constant_time.bytes_eq(derived_key, expected_key): + raise InvalidKey("Keys do not match.") |
