aboutsummaryrefslogtreecommitdiffstats
path: root/src/cryptography/hazmat/primitives/kdf/hkdf.py
diff options
context:
space:
mode:
authorDonald Stufft <donald@stufft.io>2014-11-07 19:17:08 -0500
committerDonald Stufft <donald@stufft.io>2014-11-13 07:56:31 -0500
commitc62a78c015cf7aeb0c05bce82ef14cd86fe0b0fc (patch)
tree55482d6f2e98ff65f2174294f64ad96b7be68717 /src/cryptography/hazmat/primitives/kdf/hkdf.py
parentd9f137db78d451ecb6ef7925b7dec0139ca59898 (diff)
downloadcryptography-c62a78c015cf7aeb0c05bce82ef14cd86fe0b0fc.tar.gz
cryptography-c62a78c015cf7aeb0c05bce82ef14cd86fe0b0fc.tar.bz2
cryptography-c62a78c015cf7aeb0c05bce82ef14cd86fe0b0fc.zip
Move the cryptography package into a src/ subdirectory
Due to differences in how py.test determines which module to ``import`` the test suite actually runs against the cryptography which is in the *current* directory instead of the cryptography which is installed. The problem essentially boils down to when there is a tests/__init__.py then py.test adds the current directory to the front of the sys.path, causing it to take precedence over the installed location. This means that running the tests relies on the implicit compile that CFFI does instead of testing against what people will actually be runnning, which is the module compiled by setup.py.
Diffstat (limited to 'src/cryptography/hazmat/primitives/kdf/hkdf.py')
-rw-r--r--src/cryptography/hazmat/primitives/kdf/hkdf.py124
1 files changed, 124 insertions, 0 deletions
diff --git a/src/cryptography/hazmat/primitives/kdf/hkdf.py b/src/cryptography/hazmat/primitives/kdf/hkdf.py
new file mode 100644
index 00000000..04d02b26
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/kdf/hkdf.py
@@ -0,0 +1,124 @@
+# 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 (
+ AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+)
+from cryptography.hazmat.backends.interfaces import HMACBackend
+from cryptography.hazmat.primitives import constant_time, hmac, interfaces
+
+
+@utils.register_interface(interfaces.KeyDerivationFunction)
+class HKDF(object):
+ def __init__(self, algorithm, length, salt, info, backend):
+ if not isinstance(backend, HMACBackend):
+ raise UnsupportedAlgorithm(
+ "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:
+ raise ValueError(
+ "Can not derive keys larger than {0} octets.".format(
+ max_length
+ ))
+
+ self._length = length
+
+ 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._used = False
+
+ def _expand(self, key_material):
+ output = [b""]
+ counter = 1
+
+ while (self._algorithm.digest_size // 8) * len(output) < self._length:
+ h = hmac.HMAC(key_material, self._algorithm, backend=self._backend)
+ h.update(output[-1])
+ h.update(self._info)
+ h.update(six.int2byte(counter))
+ output.append(h.finalize())
+ counter += 1
+
+ return b"".join(output)[:self._length]
+
+ def derive(self, 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(key_material)
+
+ def verify(self, key_material, expected_key):
+ if not constant_time.bytes_eq(self.derive(key_material), expected_key):
+ raise InvalidKey