diff options
Diffstat (limited to 'cryptography/hazmat')
-rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 22 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/bignum.py | 29 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/interfaces.py | 13 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/kdf/hkdf.py | 91 |
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 |