aboutsummaryrefslogtreecommitdiffstats
path: root/cryptography/hazmat
diff options
context:
space:
mode:
Diffstat (limited to 'cryptography/hazmat')
-rw-r--r--cryptography/hazmat/backends/openssl/backend.py22
-rw-r--r--cryptography/hazmat/bindings/openssl/bignum.py29
-rw-r--r--cryptography/hazmat/primitives/interfaces.py13
-rw-r--r--cryptography/hazmat/primitives/kdf/hkdf.py91
4 files changed, 149 insertions, 6 deletions
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index cf931dab..67b365fa 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -177,17 +177,30 @@ class Backend(object):
return self._ffi.buffer(buf)[:]
+ def _err_string(self, code):
+ err_buf = self._ffi.new("char[]", 256)
+ self._lib.ERR_error_string_n(code, err_buf, 256)
+ return self._ffi.string(err_buf, 256)[:]
+
def _handle_error(self, mode):
code = self._lib.ERR_get_error()
if not code and isinstance(mode, GCM):
raise InvalidTag
assert code != 0
+
+ # consume any remaining errors on the stack
+ ignored_code = None
+ while ignored_code != 0:
+ ignored_code = self._lib.ERR_get_error()
+
+ # raise the first error we found
+ return self._handle_error_code(code)
+
+ def _handle_error_code(self, code):
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:
@@ -203,7 +216,10 @@ class Backend(object):
)
raise InternalError(
- "Unknown error code from OpenSSL, you should probably file a bug."
+ "Unknown error code {0} from OpenSSL, "
+ "you should probably file a bug. {1}".format(
+ code, self._err_string(code)
+ )
)
diff --git a/cryptography/hazmat/bindings/openssl/bignum.py b/cryptography/hazmat/bindings/openssl/bignum.py
index 6545f329..e843099e 100644
--- a/cryptography/hazmat/bindings/openssl/bignum.py
+++ b/cryptography/hazmat/bindings/openssl/bignum.py
@@ -16,6 +16,7 @@ INCLUDES = """
"""
TYPES = """
+typedef ... BN_CTX;
typedef ... BIGNUM;
/*
* TODO: This typedef is wrong.
@@ -41,7 +42,13 @@ FUNCTIONS = """
BIGNUM *BN_new(void);
void BN_free(BIGNUM *);
+BIGNUM *BN_copy(BIGNUM *, const BIGNUM *);
+BIGNUM *BN_dup(const BIGNUM *);
+
int BN_set_word(BIGNUM *, BN_ULONG);
+BN_ULONG BN_get_word(const BIGNUM *);
+
+const BIGNUM *BN_value_one(void);
char *BN_bn2hex(const BIGNUM *);
int BN_hex2bn(BIGNUM **, const char *);
@@ -51,9 +58,31 @@ int BN_bn2bin(const BIGNUM *, unsigned char *);
BIGNUM *BN_bin2bn(const unsigned char *, int, BIGNUM *);
int BN_num_bits(const BIGNUM *);
+
+int BN_add(BIGNUM *, const BIGNUM *, const BIGNUM *);
+int BN_sub(BIGNUM *, const BIGNUM *, const BIGNUM *);
+int BN_mul(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *);
+int BN_sqr(BIGNUM *, const BIGNUM *, BN_CTX *);
+int BN_div(BIGNUM *, BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *);
+int BN_nnmod(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *);
+int BN_mod_add(BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *,
+ BN_CTX *);
+int BN_mod_sub(BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *,
+ BN_CTX *);
+int BN_mod_mul(BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *,
+ BN_CTX *);
+int BN_mod_sqr(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *);
+int BN_exp(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *);
+int BN_mod_exp(BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *,
+ BN_CTX *);
+int BN_gcd(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *);
+BIGNUM *BN_mod_inverse(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *);
"""
MACROS = """
+int BN_zero(BIGNUM *);
+int BN_one(BIGNUM *);
+int BN_mod(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *);
"""
CUSTOMIZATIONS = """
diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py
index 1a27644f..460aab76 100644
--- a/cryptography/hazmat/primitives/interfaces.py
+++ b/cryptography/hazmat/primitives/interfaces.py
@@ -185,7 +185,13 @@ class RSAPrivateKey(six.with_metaclass(abc.ABCMeta)):
"""
@abc.abstractproperty
- def key_length(self):
+ def private_exponent(self):
+ """
+ The private exponent of the RSA key.
+ """
+
+ @abc.abstractproperty
+ def key_size(self):
"""
The bit length of the public modulus.
"""
@@ -217,7 +223,8 @@ class RSAPrivateKey(six.with_metaclass(abc.ABCMeta)):
@abc.abstractproperty
def d(self):
"""
- The private exponent. This can be calculated using p and q.
+ The private exponent. This can be calculated using p and q. Alias for
+ private_exponent.
"""
@abc.abstractproperty
@@ -241,7 +248,7 @@ class RSAPublicKey(six.with_metaclass(abc.ABCMeta)):
"""
@abc.abstractproperty
- def key_length(self):
+ def key_size(self):
"""
The bit length of the public modulus.
"""
diff --git a/cryptography/hazmat/primitives/kdf/hkdf.py b/cryptography/hazmat/primitives/kdf/hkdf.py
new file mode 100644
index 00000000..af15b64d
--- /dev/null
+++ b/cryptography/hazmat/primitives/kdf/hkdf.py
@@ -0,0 +1,91 @@
+# 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.
+
+import six
+
+from cryptography import utils
+from cryptography.exceptions import AlreadyFinalized, InvalidKey
+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):
+ self._algorithm = algorithm
+
+ 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 isinstance(salt, six.text_type):
+ raise TypeError(
+ "Unicode-objects must be encoded before using them as a salt.")
+
+ if salt is None:
+ salt = b"\x00" * (self._algorithm.digest_size // 8)
+
+ self._salt = salt
+
+ if isinstance(info, six.text_type):
+ raise TypeError(
+ "Unicode-objects must be encoded before using them as info.")
+
+ if info is None:
+ info = b""
+
+ self._info = info
+ self._backend = backend
+
+ self._used = False
+
+ def _extract(self, key_material):
+ h = hmac.HMAC(self._salt, self._algorithm, backend=self._backend)
+ h.update(key_material)
+ return h.finalize()
+
+ 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 isinstance(key_material, six.text_type):
+ raise TypeError(
+ "Unicode-objects must be encoded before using them as key "
+ "material."
+ )
+
+ if self._used:
+ raise AlreadyFinalized
+
+ self._used = True
+ return self._expand(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