diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2014-03-08 11:09:49 -0400 |
---|---|---|
committer | Paul Kehrer <paul.l.kehrer@gmail.com> | 2014-03-08 11:09:49 -0400 |
commit | bf2a9d9545f39ad0dd9b9c9c4aa2f7f2b5669f0f (patch) | |
tree | 083cc465c6fabdb61ff69aadc33b31e8617f2136 /cryptography | |
parent | dee5c25d35c53885698bca42015c9f7bbfb27baa (diff) | |
parent | 78c2f2d2c0a40d20edcaf37c33e91224af3ecbb6 (diff) | |
download | cryptography-bf2a9d9545f39ad0dd9b9c9c4aa2f7f2b5669f0f.tar.gz cryptography-bf2a9d9545f39ad0dd9b9c9c4aa2f7f2b5669f0f.tar.bz2 cryptography-bf2a9d9545f39ad0dd9b9c9c4aa2f7f2b5669f0f.zip |
Merge branch 'master' into idea-bespoke-vectors
* master: (246 commits)
Fixed python3 incompatibility
Removed dependency on setuptools for version check
don't need to move these definitions
conditional NIDs for 0.9.8e
x509 changes for 0.9.8e support
more changes for 0.9.8e support, this time in the ssl.h headers
macro switches in evp for 0.9.8e
bind some error constants conditionally for 0.9.8e support
BIO macro switch for 0.9.8e support
move some nids
conditionally bind AES_wrap/unwrap for 0.9.8e support
Add GPG key fingerprint for lvh
change comparison to be easier to read
ridiculous workaround time
whoops
Missing imports
Convert stuff
Add binding for DSA_new
Fix drop in coverage levels by removing branches
Added check to turn of CC backend for OS X version < 10.8
...
Conflicts:
docs/development/test-vectors.rst
Diffstat (limited to 'cryptography')
30 files changed, 1036 insertions, 137 deletions
diff --git a/cryptography/__about__.py b/cryptography/__about__.py index ee1d8a05..b175824f 100644 --- a/cryptography/__about__.py +++ b/cryptography/__about__.py @@ -18,11 +18,11 @@ __all__ = [ ] __title__ = "cryptography" -__summary__ = ("cryptography is a package designed to expose cryptographic " - "recipes and primitives to Python developers.") +__summary__ = ("cryptography is a package which provides cryptographic recipes" + " and primitives to Python developers.") __uri__ = "https://github.com/pyca/cryptography" -__version__ = "0.2.dev1" +__version__ = "0.3.dev1" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" diff --git a/cryptography/exceptions.py b/cryptography/exceptions.py index e2542a1f..a26dbe18 100644 --- a/cryptography/exceptions.py +++ b/cryptography/exceptions.py @@ -16,6 +16,18 @@ class UnsupportedAlgorithm(Exception): pass +class UnsupportedCipher(UnsupportedAlgorithm): + pass + + +class UnsupportedHash(UnsupportedAlgorithm): + pass + + +class UnsupportedPadding(UnsupportedAlgorithm): + pass + + class AlreadyFinalized(Exception): pass @@ -42,3 +54,7 @@ class InternalError(Exception): class InvalidKey(Exception): pass + + +class InvalidToken(Exception): + pass diff --git a/cryptography/hazmat/backends/__init__.py b/cryptography/hazmat/backends/__init__.py index 41d260a8..406b37e5 100644 --- a/cryptography/hazmat/backends/__init__.py +++ b/cryptography/hazmat/backends/__init__.py @@ -17,12 +17,14 @@ from cryptography.hazmat.bindings.commoncrypto.binding import ( Binding as CommonCryptoBinding ) -_ALL_BACKENDS = [openssl.backend] +_ALL_BACKENDS = [] if CommonCryptoBinding.is_available(): from cryptography.hazmat.backends import commoncrypto _ALL_BACKENDS.append(commoncrypto.backend) +_ALL_BACKENDS.append(openssl.backend) + _default_backend = MultiBackend(_ALL_BACKENDS) diff --git a/cryptography/hazmat/backends/commoncrypto/backend.py b/cryptography/hazmat/backends/commoncrypto/backend.py index 5c08a356..53228b31 100644 --- a/cryptography/hazmat/backends/commoncrypto/backend.py +++ b/cryptography/hazmat/backends/commoncrypto/backend.py @@ -17,7 +17,7 @@ from collections import namedtuple from cryptography import utils from cryptography.exceptions import ( - UnsupportedAlgorithm, InvalidTag, InternalError + InvalidTag, InternalError, UnsupportedCipher, UnsupportedHash ) from cryptography.hazmat.backends.interfaces import ( HashBackend, HMACBackend, CipherBackend, PBKDF2HMACBackend @@ -202,7 +202,8 @@ class Backend(object): (CBC, self._lib.kCCModeCBC), (ECB, self._lib.kCCModeECB), (CFB, self._lib.kCCModeCFB), - (OFB, self._lib.kCCModeOFB) + (OFB, self._lib.kCCModeOFB), + (CTR, self._lib.kCCModeCTR) ]: self._register_cipher_adapter( CAST5, @@ -272,7 +273,7 @@ class _CipherContext(object): try: cipher_enum, mode_enum = registry[type(cipher), type(mode)] except KeyError: - raise UnsupportedAlgorithm( + raise UnsupportedCipher( "cipher {0} in {1} mode is not supported " "by this backend".format( cipher.name, mode.name if mode else mode) @@ -345,7 +346,7 @@ class _GCMCipherContext(object): try: cipher_enum, mode_enum = registry[type(cipher), type(mode)] except KeyError: - raise UnsupportedAlgorithm( + raise UnsupportedCipher( "cipher {0} in {1} mode is not supported " "by this backend".format( cipher.name, mode.name if mode else mode) @@ -419,7 +420,7 @@ class _HashContext(object): try: methods = self._backend._hash_mapping[self.algorithm.name] except KeyError: - raise UnsupportedAlgorithm( + raise UnsupportedHash( "{0} is not a supported hash on this backend".format( algorithm.name) ) @@ -462,7 +463,7 @@ class _HMACContext(object): try: alg = self._backend._supported_hmac_algorithms[algorithm.name] except KeyError: - raise UnsupportedAlgorithm( + raise UnsupportedHash( "{0} is not a supported HMAC hash on this backend".format( algorithm.name) ) diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py index b867f26a..da41532d 100644 --- a/cryptography/hazmat/backends/interfaces.py +++ b/cryptography/hazmat/backends/interfaces.py @@ -90,3 +90,27 @@ class RSABackend(six.with_metaclass(abc.ABCMeta)): Generate an RSAPrivateKey instance with public_exponent and a modulus of key_size bits. """ + + @abc.abstractmethod + def create_rsa_signature_ctx(self, private_key, padding, algorithm): + """ + Returns an object conforming to the AsymmetricSignatureContext + interface. + """ + + @abc.abstractmethod + def create_rsa_verification_ctx(self, public_key, signature, padding, + algorithm): + """ + Returns an object conforming to the AsymmetricVerificationContext + interface. + """ + + +class OpenSSLSerializationBackend(six.with_metaclass(abc.ABCMeta)): + @abc.abstractmethod + def load_openssl_pem_private_key(self, data, password): + """ + Load a private key from PEM encoded data, using password if the data + is encrypted. + """ diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py index 4de02026..cca82a59 100644 --- a/cryptography/hazmat/backends/multibackend.py +++ b/cryptography/hazmat/backends/multibackend.py @@ -14,7 +14,9 @@ from __future__ import absolute_import, division, print_function from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm +from cryptography.exceptions import ( + UnsupportedAlgorithm, UnsupportedCipher, UnsupportedHash +) from cryptography.hazmat.backends.interfaces import ( CipherBackend, HashBackend, HMACBackend, PBKDF2HMACBackend, RSABackend ) @@ -24,6 +26,7 @@ from cryptography.hazmat.backends.interfaces import ( @utils.register_interface(HashBackend) @utils.register_interface(HMACBackend) @utils.register_interface(PBKDF2HMACBackend) +@utils.register_interface(RSABackend) class MultiBackend(object): name = "multibackend" @@ -45,17 +48,17 @@ class MultiBackend(object): for b in self._filtered_backends(CipherBackend): try: return b.create_symmetric_encryption_ctx(algorithm, mode) - except UnsupportedAlgorithm: + except UnsupportedCipher: pass - raise UnsupportedAlgorithm + raise UnsupportedCipher def create_symmetric_decryption_ctx(self, algorithm, mode): for b in self._filtered_backends(CipherBackend): try: return b.create_symmetric_decryption_ctx(algorithm, mode) - except UnsupportedAlgorithm: + except UnsupportedCipher: pass - raise UnsupportedAlgorithm + raise UnsupportedCipher def hash_supported(self, algorithm): return any( @@ -67,9 +70,9 @@ class MultiBackend(object): for b in self._filtered_backends(HashBackend): try: return b.create_hash_ctx(algorithm) - except UnsupportedAlgorithm: + except UnsupportedHash: pass - raise UnsupportedAlgorithm + raise UnsupportedHash def hmac_supported(self, algorithm): return any( @@ -81,9 +84,9 @@ class MultiBackend(object): for b in self._filtered_backends(HMACBackend): try: return b.create_hmac_ctx(key, algorithm) - except UnsupportedAlgorithm: + except UnsupportedHash: pass - raise UnsupportedAlgorithm + raise UnsupportedHash def pbkdf2_hmac_supported(self, algorithm): return any( @@ -98,11 +101,23 @@ class MultiBackend(object): return b.derive_pbkdf2_hmac( algorithm, length, salt, iterations, key_material ) - except UnsupportedAlgorithm: + except UnsupportedHash: pass - raise UnsupportedAlgorithm + raise UnsupportedHash def generate_rsa_private_key(self, public_exponent, key_size): for b in self._filtered_backends(RSABackend): return b.generate_rsa_private_key(public_exponent, key_size) raise UnsupportedAlgorithm + + def create_rsa_signature_ctx(self, private_key, padding, algorithm): + for b in self._filtered_backends(RSABackend): + return b.create_rsa_signature_ctx(private_key, padding, algorithm) + raise UnsupportedAlgorithm + + def create_rsa_verification_ctx(self, public_key, signature, padding, + algorithm): + for b in self._filtered_backends(RSABackend): + return b.create_rsa_verification_ctx(public_key, signature, + padding, algorithm) + raise UnsupportedAlgorithm diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 8a4aeac5..b4625aae 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -13,11 +13,13 @@ from __future__ import absolute_import, division, print_function +import collections import itertools from cryptography import utils from cryptography.exceptions import ( - UnsupportedAlgorithm, InvalidTag, InternalError + InvalidTag, InternalError, AlreadyFinalized, UnsupportedCipher, + UnsupportedHash, UnsupportedPadding, InvalidSignature ) from cryptography.hazmat.backends.interfaces import ( CipherBackend, HashBackend, HMACBackend, PBKDF2HMACBackend, RSABackend @@ -33,6 +35,10 @@ from cryptography.hazmat.primitives.ciphers.modes import ( ) +_OpenSSLError = collections.namedtuple("_OpenSSLError", + ["code", "lib", "func", "reason"]) + + @utils.register_interface(CipherBackend) @utils.register_interface(HashBackend) @utils.register_interface(HMACBackend) @@ -205,7 +211,7 @@ class Backend(object): assert res == 1 else: if not isinstance(algorithm, hashes.SHA1): - raise UnsupportedAlgorithm( + raise UnsupportedHash( "This version of OpenSSL only supports PBKDF2HMAC with " "SHA1" ) @@ -227,43 +233,25 @@ class Backend(object): 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) - - 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: - raise ValueError( - "The length of the provided data is not a multiple of " - "the block length" - ) - elif func == self._lib.EVP_F_EVP_DECRYPTFINAL_EX: - if reason == self._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: - raise ValueError( - "The length of the provided data is not a multiple of " - "the block length" - ) - - raise InternalError( + def _consume_errors(self): + errors = [] + while True: + code = self._lib.ERR_get_error() + if code == 0: + break + + lib = self._lib.ERR_GET_LIB(code) + func = self._lib.ERR_GET_FUNC(code) + reason = self._lib.ERR_GET_REASON(code) + + errors.append(_OpenSSLError(code, lib, func, reason)) + return errors + + def _unknown_error(self, error): + return InternalError( "Unknown error code {0} from OpenSSL, " "you should probably file a bug. {1}".format( - code, self._err_string(code) + error.code, self._err_string(error.code) ) ) @@ -274,6 +262,20 @@ class Backend(object): self._lib.OPENSSL_free(hex_cdata) return int(hex_str, 16) + def _int_to_bn(self, num): + """ + Converts a python integer to a BIGNUM. The returned BIGNUM will not + be garbage collected (to support adding them to structs that take + ownership of the object). Be sure to register it for GC if it will + be discarded after use. + """ + hex_num = hex(num).rstrip("L").lstrip("0x").encode("ascii") or b"0" + bn_ptr = self._ffi.new("BIGNUM **") + res = self._lib.BN_hex2bn(bn_ptr, hex_num) + assert res != 0 + assert bn_ptr[0] != self._ffi.NULL + return bn_ptr[0] + def generate_rsa_private_key(self, public_exponent, key_size): if public_exponent < 3: raise ValueError("public_exponent must be >= 3") @@ -284,18 +286,15 @@ class Backend(object): if key_size < 512: raise ValueError("key_size must be at least 512-bits") - ctx = backend._lib.RSA_new() - ctx = backend._ffi.gc(ctx, backend._lib.RSA_free) - - bn = backend._lib.BN_new() - assert bn != self._ffi.NULL - bn = backend._ffi.gc(bn, backend._lib.BN_free) + ctx = self._lib.RSA_new() + assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.RSA_free) - res = backend._lib.BN_set_word(bn, public_exponent) - assert res == 1 + bn = self._int_to_bn(public_exponent) + bn = self._ffi.gc(bn, self._lib.BN_free) - res = backend._lib.RSA_generate_key_ex( - ctx, key_size, bn, backend._ffi.NULL + res = self._lib.RSA_generate_key_ex( + ctx, key_size, bn, self._ffi.NULL ) assert res == 1 @@ -310,6 +309,36 @@ class Backend(object): modulus=self._bn_to_int(ctx.n), ) + def _rsa_cdata_from_private_key(self, private_key): + ctx = self._lib.RSA_new() + assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.RSA_free) + ctx.p = self._int_to_bn(private_key.p) + ctx.q = self._int_to_bn(private_key.q) + ctx.d = self._int_to_bn(private_key.d) + ctx.e = self._int_to_bn(private_key.e) + ctx.n = self._int_to_bn(private_key.n) + ctx.dmp1 = self._int_to_bn(private_key.dmp1) + ctx.dmq1 = self._int_to_bn(private_key.dmq1) + ctx.iqmp = self._int_to_bn(private_key.iqmp) + return ctx + + def _rsa_cdata_from_public_key(self, public_key): + ctx = self._lib.RSA_new() + assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.RSA_free) + ctx.e = self._int_to_bn(public_key.e) + ctx.n = self._int_to_bn(public_key.n) + return ctx + + def create_rsa_signature_ctx(self, private_key, padding, algorithm): + return _RSASignatureContext(self, private_key, padding, algorithm) + + def create_rsa_verification_ctx(self, public_key, signature, padding, + algorithm): + return _RSAVerificationContext(self, public_key, signature, padding, + algorithm) + class GetCipherByName(object): def __init__(self, fmt): @@ -348,7 +377,7 @@ class _CipherContext(object): try: adapter = registry[type(cipher), type(mode)] except KeyError: - raise UnsupportedAlgorithm( + raise UnsupportedCipher( "cipher {0} in {1} mode is not supported " "by this backend".format( cipher.name, mode.name if mode else mode) @@ -356,7 +385,7 @@ class _CipherContext(object): evp_cipher = adapter(self._backend, cipher, mode) if evp_cipher == self._backend._ffi.NULL: - raise UnsupportedAlgorithm( + raise UnsupportedCipher( "cipher {0} in {1} mode is not supported " "by this backend".format( cipher.name, mode.name if mode else mode) @@ -409,6 +438,15 @@ class _CipherContext(object): self._ctx = ctx def update(self, data): + # OpenSSL 0.9.8e has an assertion in its EVP code that causes it + # to SIGABRT if you call update with an empty byte string. This can be + # removed when we drop support for 0.9.8e (CentOS/RHEL 5). This branch + # should be taken only when length is zero and mode is not GCM because + # AES GCM can return improper tag values if you don't call update + # with empty plaintext when authenticating AAD for ...reasons. + if len(data) == 0 and not isinstance(self._mode, GCM): + return b"" + buf = self._backend._ffi.new("unsigned char[]", len(data) + self._block_size - 1) outlen = self._backend._ffi.new("int *") @@ -422,7 +460,28 @@ class _CipherContext(object): outlen = self._backend._ffi.new("int *") res = self._backend._lib.EVP_CipherFinal_ex(self._ctx, buf, outlen) if res == 0: - self._backend._handle_error(self._mode) + errors = self._backend._consume_errors() + + if not errors and isinstance(self._mode, GCM): + raise InvalidTag + + assert errors + + if errors[0][1:] == ( + self._backend._lib.ERR_LIB_EVP, + self._backend._lib.EVP_F_EVP_ENCRYPTFINAL_EX, + self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH + ) or errors[0][1:] == ( + self._backend._lib.ERR_LIB_EVP, + self._backend._lib.EVP_F_EVP_DECRYPTFINAL_EX, + self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH + ): + raise ValueError( + "The length of the provided data is not a multiple of " + "the block length." + ) + else: + raise self._backend._unknown_error(errors[0]) if (isinstance(self._mode, GCM) and self._operation == self._ENCRYPT): @@ -467,7 +526,7 @@ class _HashContext(object): evp_md = self._backend._lib.EVP_get_digestbyname( algorithm.name.encode("ascii")) if evp_md == self._backend._ffi.NULL: - raise UnsupportedAlgorithm( + raise UnsupportedHash( "{0} is not a supported hash on this backend".format( algorithm.name) ) @@ -492,13 +551,14 @@ class _HashContext(object): def finalize(self): buf = self._backend._ffi.new("unsigned char[]", - self.algorithm.digest_size) - res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, - self._backend._ffi.NULL) + self._backend._lib.EVP_MAX_MD_SIZE) + outlen = self._backend._ffi.new("unsigned int *") + res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen) assert res != 0 + assert outlen[0] == self.algorithm.digest_size res = self._backend._lib.EVP_MD_CTX_cleanup(self._ctx) assert res == 1 - return self._backend._ffi.buffer(buf)[:] + return self._backend._ffi.buffer(buf)[:outlen[0]] @utils.register_interface(interfaces.HashContext) @@ -516,7 +576,7 @@ class _HMACContext(object): evp_md = self._backend._lib.EVP_get_digestbyname( algorithm.name.encode('ascii')) if evp_md == self._backend._ffi.NULL: - raise UnsupportedAlgorithm( + raise UnsupportedHash( "{0} is not a supported hash on this backend".format( algorithm.name) ) @@ -550,15 +610,208 @@ class _HMACContext(object): def finalize(self): buf = self._backend._ffi.new("unsigned char[]", - self.algorithm.digest_size) - buflen = self._backend._ffi.new("unsigned int *", - self.algorithm.digest_size) + self._backend._lib.EVP_MAX_MD_SIZE) + outlen = self._backend._ffi.new("unsigned int *") res = self._backend._lib.Cryptography_HMAC_Final( - self._ctx, buf, buflen + self._ctx, buf, outlen ) assert res != 0 + assert outlen[0] == self.algorithm.digest_size self._backend._lib.HMAC_CTX_cleanup(self._ctx) + return self._backend._ffi.buffer(buf)[:outlen[0]] + + +@utils.register_interface(interfaces.AsymmetricSignatureContext) +class _RSASignatureContext(object): + def __init__(self, backend, private_key, padding, algorithm): + self._backend = backend + self._private_key = private_key + if not isinstance(padding, interfaces.AsymmetricPadding): + raise TypeError( + "Expected provider of interfaces.AsymmetricPadding") + + if padding.name == "EMSA-PKCS1-v1_5": + if self._backend._lib.Cryptography_HAS_PKEY_CTX: + self._finalize_method = self._finalize_pkey_ctx + self._padding_enum = self._backend._lib.RSA_PKCS1_PADDING + else: + self._finalize_method = self._finalize_pkcs1 + else: + raise UnsupportedPadding( + "{0} is not supported by this backend".format(padding.name) + ) + + self._padding = padding + self._algorithm = algorithm + self._hash_ctx = _HashContext(backend, self._algorithm) + + def update(self, data): + if self._hash_ctx is None: + raise AlreadyFinalized("Context has already been finalized") + + self._hash_ctx.update(data) + + def finalize(self): + if self._hash_ctx is None: + raise AlreadyFinalized("Context has already been finalized") + evp_pkey = self._backend._lib.EVP_PKEY_new() + assert evp_pkey != self._backend._ffi.NULL + evp_pkey = backend._ffi.gc(evp_pkey, backend._lib.EVP_PKEY_free) + rsa_cdata = backend._rsa_cdata_from_private_key(self._private_key) + res = self._backend._lib.RSA_blinding_on( + rsa_cdata, self._backend._ffi.NULL) + assert res == 1 + res = self._backend._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata) + assert res == 1 + evp_md = self._backend._lib.EVP_get_digestbyname( + self._algorithm.name.encode("ascii")) + assert evp_md != self._backend._ffi.NULL + pkey_size = self._backend._lib.EVP_PKEY_size(evp_pkey) + assert pkey_size > 0 + + return self._finalize_method(evp_pkey, pkey_size, rsa_cdata, evp_md) + + def _finalize_pkey_ctx(self, evp_pkey, pkey_size, rsa_cdata, evp_md): + pkey_ctx = self._backend._lib.EVP_PKEY_CTX_new( + evp_pkey, self._backend._ffi.NULL + ) + assert pkey_ctx != self._backend._ffi.NULL + res = self._backend._lib.EVP_PKEY_sign_init(pkey_ctx) + assert res == 1 + res = self._backend._lib.EVP_PKEY_CTX_set_signature_md( + pkey_ctx, evp_md) + assert res > 0 + + res = self._backend._lib.EVP_PKEY_CTX_set_rsa_padding( + pkey_ctx, self._padding_enum) + assert res > 0 + data_to_sign = self._hash_ctx.finalize() + self._hash_ctx = None + buflen = self._backend._ffi.new("size_t *") + res = self._backend._lib.EVP_PKEY_sign( + pkey_ctx, + self._backend._ffi.NULL, + buflen, + data_to_sign, + len(data_to_sign) + ) + assert res == 1 + buf = self._backend._ffi.new("unsigned char[]", buflen[0]) + res = self._backend._lib.EVP_PKEY_sign( + pkey_ctx, buf, buflen, data_to_sign, len(data_to_sign)) + assert res == 1 return self._backend._ffi.buffer(buf)[:] + def _finalize_pkcs1(self, evp_pkey, pkey_size, rsa_cdata, evp_md): + sig_buf = self._backend._ffi.new("char[]", pkey_size) + sig_len = self._backend._ffi.new("unsigned int *") + res = self._backend._lib.EVP_SignFinal( + self._hash_ctx._ctx, + sig_buf, + sig_len, + evp_pkey + ) + self._hash_ctx.finalize() + self._hash_ctx = None + assert res == 1 + return self._backend._ffi.buffer(sig_buf)[:sig_len[0]] + + +@utils.register_interface(interfaces.AsymmetricVerificationContext) +class _RSAVerificationContext(object): + def __init__(self, backend, public_key, signature, padding, algorithm): + self._backend = backend + self._public_key = public_key + self._signature = signature + if not isinstance(padding, interfaces.AsymmetricPadding): + raise TypeError( + "Expected provider of interfaces.AsymmetricPadding") + + if padding.name == "EMSA-PKCS1-v1_5": + if self._backend._lib.Cryptography_HAS_PKEY_CTX: + self._verify_method = self._verify_pkey_ctx + self._padding_enum = self._backend._lib.RSA_PKCS1_PADDING + else: + self._verify_method = self._verify_pkcs1 + else: + raise UnsupportedPadding + + self._padding = padding + self._algorithm = algorithm + self._hash_ctx = _HashContext(backend, self._algorithm) + + def update(self, data): + if self._hash_ctx is None: + raise AlreadyFinalized("Context has already been finalized") + + self._hash_ctx.update(data) + + def verify(self): + if self._hash_ctx is None: + raise AlreadyFinalized("Context has already been finalized") + + evp_pkey = self._backend._lib.EVP_PKEY_new() + assert evp_pkey != self._backend._ffi.NULL + evp_pkey = backend._ffi.gc(evp_pkey, backend._lib.EVP_PKEY_free) + rsa_cdata = backend._rsa_cdata_from_public_key(self._public_key) + res = self._backend._lib.RSA_blinding_on( + rsa_cdata, self._backend._ffi.NULL) + assert res == 1 + res = self._backend._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata) + assert res == 1 + evp_md = self._backend._lib.EVP_get_digestbyname( + self._algorithm.name.encode("ascii")) + assert evp_md != self._backend._ffi.NULL + + self._verify_method(rsa_cdata, evp_pkey, evp_md) + + def _verify_pkey_ctx(self, rsa_cdata, evp_pkey, evp_md): + pkey_ctx = self._backend._lib.EVP_PKEY_CTX_new( + evp_pkey, self._backend._ffi.NULL + ) + assert pkey_ctx != self._backend._ffi.NULL + res = self._backend._lib.EVP_PKEY_verify_init(pkey_ctx) + assert res == 1 + res = self._backend._lib.EVP_PKEY_CTX_set_signature_md( + pkey_ctx, evp_md) + assert res > 0 + + res = self._backend._lib.EVP_PKEY_CTX_set_rsa_padding( + pkey_ctx, self._padding_enum) + assert res > 0 + data_to_verify = self._hash_ctx.finalize() + self._hash_ctx = None + res = self._backend._lib.EVP_PKEY_verify( + pkey_ctx, + self._signature, + len(self._signature), + data_to_verify, + len(data_to_verify) + ) + # The previous call can return negative numbers in the event of an + # error. This is not a signature failure but we need to fail if it + # occurs. + assert res >= 0 + if res == 0: + assert self._backend._consume_errors() + raise InvalidSignature + + def _verify_pkcs1(self, rsa_cdata, evp_pkey, evp_md): + res = self._backend._lib.EVP_VerifyFinal( + self._hash_ctx._ctx, + self._signature, + len(self._signature), + evp_pkey + ) + self._hash_ctx.finalize() + self._hash_ctx = None + # The previous call can return negative numbers in the event of an + # error. This is not a signature failure but we need to fail if it + # occurs. + assert res >= 0 + if res == 0: + assert self._backend._consume_errors() + raise InvalidSignature + backend = Backend() diff --git a/cryptography/hazmat/bindings/commoncrypto/binding.py b/cryptography/hazmat/bindings/commoncrypto/binding.py index 45c0eaad..ee809425 100644 --- a/cryptography/hazmat/bindings/commoncrypto/binding.py +++ b/cryptography/hazmat/bindings/commoncrypto/binding.py @@ -14,6 +14,7 @@ from __future__ import absolute_import, division, print_function import sys +import platform from cryptography.hazmat.bindings.utils import build_ffi @@ -46,4 +47,5 @@ class Binding(object): @classmethod def is_available(cls): - return sys.platform == "darwin" + return sys.platform == "darwin" and list(map( + int, platform.mac_ver()[0].split("."))) >= [10, 8, 0] diff --git a/cryptography/hazmat/bindings/openssl/aes.py b/cryptography/hazmat/bindings/openssl/aes.py index 6cbcd577..95ed5271 100644 --- a/cryptography/hazmat/bindings/openssl/aes.py +++ b/cryptography/hazmat/bindings/openssl/aes.py @@ -16,6 +16,8 @@ INCLUDES = """ """ TYPES = """ +static const int Cryptography_HAS_AES_WRAP; + struct aes_key_st { ...; }; @@ -25,16 +27,34 @@ typedef struct aes_key_st AES_KEY; FUNCTIONS = """ int AES_set_encrypt_key(const unsigned char *, const int, AES_KEY *); int AES_set_decrypt_key(const unsigned char *, const int, AES_KEY *); +""" + +MACROS = """ +/* these can be moved back to FUNCTIONS once we drop support for 0.9.8h. + This should be when we drop RHEL/CentOS 5, which is on 0.9.8e. */ int AES_wrap_key(AES_KEY *, const unsigned char *, unsigned char *, const unsigned char *, unsigned int); int AES_unwrap_key(AES_KEY *, const unsigned char *, unsigned char *, const unsigned char *, unsigned int); """ -MACROS = """ -""" - CUSTOMIZATIONS = """ +// OpenSSL 0.9.8h+ +#if OPENSSL_VERSION_NUMBER >= 0x0090808fL +static const long Cryptography_HAS_AES_WRAP = 1; +#else +static const long Cryptography_HAS_AES_WRAP = 0; +int (*AES_wrap_key)(AES_KEY *, const unsigned char *, unsigned char *, + const unsigned char *, unsigned int) = NULL; +int (*AES_unwrap_key)(AES_KEY *, const unsigned char *, unsigned char *, + const unsigned char *, unsigned int) = NULL; +#endif + """ -CONDITIONAL_NAMES = {} +CONDITIONAL_NAMES = { + "Cryptography_HAS_AES_WRAP": [ + "AES_wrap_key", + "AES_unwrap_key", + ], +} diff --git a/cryptography/hazmat/bindings/openssl/binding.py b/cryptography/hazmat/bindings/openssl/binding.py index 714ecc07..0469a1ea 100644 --- a/cryptography/hazmat/bindings/openssl/binding.py +++ b/cryptography/hazmat/bindings/openssl/binding.py @@ -98,7 +98,7 @@ class Binding(object): _OSX_PRE_INCLUDE, _OSX_POST_INCLUDE, libraries) res = cls.lib.Cryptography_add_osrandom_engine() - assert res == 1 + assert res != 0 @classmethod def is_available(cls): diff --git a/cryptography/hazmat/bindings/openssl/bio.py b/cryptography/hazmat/bindings/openssl/bio.py index 279ad223..28172689 100644 --- a/cryptography/hazmat/bindings/openssl/bio.py +++ b/cryptography/hazmat/bindings/openssl/bio.py @@ -105,7 +105,6 @@ BIO *BIO_push(BIO *, BIO *); BIO *BIO_pop(BIO *); BIO *BIO_next(BIO *); BIO *BIO_find_type(BIO *, int); -int BIO_method_type(const BIO *); BIO_METHOD *BIO_s_mem(void); BIO *BIO_new_mem_buf(void *, int); BIO_METHOD *BIO_s_file(void); @@ -168,6 +167,10 @@ long BIO_set_read_buffer_size(BIO *, long); long BIO_set_write_buffer_size(BIO *, long); long BIO_set_buffer_size(BIO *, long); long BIO_set_buffer_read_data(BIO *, void *, long); + +/* The following was a macro in 0.9.8e. Once we drop support for RHEL/CentOS 5 + we should move this back to FUNCTIONS. */ +int BIO_method_type(const BIO *); """ CUSTOMIZATIONS = """ diff --git a/cryptography/hazmat/bindings/openssl/dsa.py b/cryptography/hazmat/bindings/openssl/dsa.py index 609a33bf..664296d3 100644 --- a/cryptography/hazmat/bindings/openssl/dsa.py +++ b/cryptography/hazmat/bindings/openssl/dsa.py @@ -35,10 +35,13 @@ FUNCTIONS = """ DSA *DSA_generate_parameters(int, unsigned char *, int, int *, unsigned long *, void (*)(int, int, void *), void *); int DSA_generate_key(DSA *); +DSA *DSA_new(void); void DSA_free(DSA *); """ MACROS = """ +int DSA_generate_parameters_ex(DSA *, int, unsigned char *, int, + int *, unsigned long *, BN_GENCB *); """ CUSTOMIZATIONS = """ diff --git a/cryptography/hazmat/bindings/openssl/ec.py b/cryptography/hazmat/bindings/openssl/ec.py index 39403ff2..9d6f7cb9 100644 --- a/cryptography/hazmat/bindings/openssl/ec.py +++ b/cryptography/hazmat/bindings/openssl/ec.py @@ -23,14 +23,10 @@ TYPES = """ static const int Cryptography_HAS_EC; typedef ... EC_KEY; - -static const int NID_X9_62_prime192v1; -static const int NID_X9_62_prime192v2; -static const int NID_X9_62_prime192v3; -static const int NID_X9_62_prime239v1; -static const int NID_X9_62_prime239v2; -static const int NID_X9_62_prime239v3; -static const int NID_X9_62_prime256v1; +typedef struct { + int nid; + const char *comment; +} EC_builtin_curve; """ FUNCTIONS = """ @@ -39,14 +35,22 @@ FUNCTIONS = """ MACROS = """ EC_KEY *EC_KEY_new_by_curve_name(int); void EC_KEY_free(EC_KEY *); + +size_t EC_get_builtin_curves(EC_builtin_curve *, size_t); + """ CUSTOMIZATIONS = """ #ifdef OPENSSL_NO_EC static const long Cryptography_HAS_EC = 0; typedef void EC_KEY; +typedef struct { + int nid; + const char *comment; +} EC_builtin_curve; EC_KEY* (*EC_KEY_new_by_curve_name)(int) = NULL; void (*EC_KEY_free)(EC_KEY *) = NULL; +size_t (*EC_get_builtin_curves)(EC_builtin_curve *, size_t) = NULL; #else static const long Cryptography_HAS_EC = 1; #endif @@ -56,5 +60,6 @@ CONDITIONAL_NAMES = { "Cryptography_HAS_EC": [ "EC_KEY_new_by_curve_name", "EC_KEY_free", + "EC_get_builtin_curves", ], } diff --git a/cryptography/hazmat/bindings/openssl/err.py b/cryptography/hazmat/bindings/openssl/err.py index ddb60ef7..f21d98b6 100644 --- a/cryptography/hazmat/bindings/openssl/err.py +++ b/cryptography/hazmat/bindings/openssl/err.py @@ -17,6 +17,7 @@ INCLUDES = """ TYPES = """ static const int Cryptography_HAS_REMOVE_THREAD_STATE; +static const int Cryptography_HAS_098H_ERROR_CODES; struct ERR_string_data_st { unsigned long error; @@ -50,8 +51,6 @@ static const int ASN1_F_ASN1_TYPE_GET_OCTETSTRING; static const int ASN1_F_ASN1_UNPACK_STRING; static const int ASN1_F_ASN1_UTCTIME_SET; static const int ASN1_F_ASN1_VERIFY; -static const int ASN1_F_B64_READ_ASN1; -static const int ASN1_F_B64_WRITE_ASN1; static const int ASN1_F_BITSTR_CB; static const int ASN1_F_BN_TO_ASN1_ENUMERATED; static const int ASN1_F_BN_TO_ASN1_INTEGER; @@ -71,8 +70,6 @@ static const int ASN1_F_LONG_C2I; static const int ASN1_F_OID_MODULE_INIT; static const int ASN1_F_PARSE_TAGGING; static const int ASN1_F_PKCS5_PBE_SET; -static const int ASN1_F_SMIME_READ_ASN1; -static const int ASN1_F_SMIME_TEXT; static const int ASN1_F_X509_CINF_NEW; static const int ASN1_R_BOOLEAN_IS_WRONG_LENGTH; static const int ASN1_R_BUFFER_TOO_SMALL; @@ -86,10 +83,7 @@ static const int ASN1_R_ERROR_GETTING_TIME; static const int ASN1_R_ERROR_LOADING_SECTION; static const int ASN1_R_MSTRING_WRONG_TAG; static const int ASN1_R_NESTED_ASN1_STRING; -static const int ASN1_R_NO_CONTENT_TYPE; static const int ASN1_R_NO_MATCHING_CHOICE_TYPE; -static const int ASN1_R_NO_MULTIPART_BODY_FAILURE; -static const int ASN1_R_NO_MULTIPART_BOUNDARY; static const int ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM; static const int ASN1_R_UNKNOWN_OBJECT_TYPE; static const int ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE; @@ -151,7 +145,6 @@ static const int EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED; static const int EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH; static const int EVP_R_DECODE_ERROR; static const int EVP_R_DIFFERENT_KEY_TYPES; -static const int EVP_R_DISABLED_FOR_FIPS; static const int EVP_R_ENCODE_ERROR; static const int EVP_R_INITIALIZATION_ERROR; static const int EVP_R_INPUT_NOT_INITIALIZED; @@ -258,6 +251,16 @@ int ERR_FATAL_ERROR(unsigned long); * supporting 0.9.8 */ void ERR_remove_thread_state(const CRYPTO_THREADID *); + +/* These were added in OpenSSL 0.9.8h. When we drop support for RHEL/CentOS 5 + we should be able to move these back to TYPES. */ +static const int ASN1_F_B64_READ_ASN1; +static const int ASN1_F_B64_WRITE_ASN1; +static const int ASN1_F_SMIME_READ_ASN1; +static const int ASN1_F_SMIME_TEXT; +static const int ASN1_R_NO_CONTENT_TYPE; +static const int ASN1_R_NO_MULTIPART_BODY_FAILURE; +static const int ASN1_R_NO_MULTIPART_BOUNDARY; """ CUSTOMIZATIONS = """ @@ -266,7 +269,21 @@ static const long Cryptography_HAS_REMOVE_THREAD_STATE = 1; #else static const long Cryptography_HAS_REMOVE_THREAD_STATE = 0; typedef uint32_t CRYPTO_THREADID; -void (*ERR_remove_thread_state)(const CRYPTO_THREADID *); +void (*ERR_remove_thread_state)(const CRYPTO_THREADID *) = NULL; +#endif + +// OpenSSL 0.9.8h+ +#if OPENSSL_VERSION_NUMBER >= 0x0090808fL +static const long Cryptography_HAS_098H_ERROR_CODES = 1; +#else +static const long Cryptography_HAS_098H_ERROR_CODES = 0; +static const int ASN1_F_B64_READ_ASN1 = 0; +static const int ASN1_F_B64_WRITE_ASN1 = 0; +static const int ASN1_F_SMIME_READ_ASN1 = 0; +static const int ASN1_F_SMIME_TEXT = 0; +static const int ASN1_R_NO_CONTENT_TYPE = 0; +static const int ASN1_R_NO_MULTIPART_BODY_FAILURE = 0; +static const int ASN1_R_NO_MULTIPART_BOUNDARY = 0; #endif """ @@ -274,4 +291,13 @@ CONDITIONAL_NAMES = { "Cryptography_HAS_REMOVE_THREAD_STATE": [ "ERR_remove_thread_state" ], + "Cryptography_HAS_098H_ERROR_CODES": [ + "ASN1_F_B64_READ_ASN1", + "ASN1_F_B64_WRITE_ASN1", + "ASN1_F_SMIME_READ_ASN1", + "ASN1_F_SMIME_TEXT", + "ASN1_R_NO_CONTENT_TYPE", + "ASN1_R_NO_MULTIPART_BODY_FAILURE", + "ASN1_R_NO_MULTIPART_BOUNDARY", + ], } diff --git a/cryptography/hazmat/bindings/openssl/evp.py b/cryptography/hazmat/bindings/openssl/evp.py index 02776490..77128c47 100644 --- a/cryptography/hazmat/bindings/openssl/evp.py +++ b/cryptography/hazmat/bindings/openssl/evp.py @@ -64,8 +64,6 @@ int EVP_CipherUpdate(EVP_CIPHER_CTX *, unsigned char *, int *, const unsigned char *, int); int EVP_CipherFinal_ex(EVP_CIPHER_CTX *, unsigned char *, int *); int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *); -const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *); -int EVP_CIPHER_block_size(const EVP_CIPHER *); void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *); EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void); void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *); @@ -79,13 +77,12 @@ int EVP_DigestFinal_ex(EVP_MD_CTX *, unsigned char *, unsigned int *); int EVP_MD_CTX_cleanup(EVP_MD_CTX *); void EVP_MD_CTX_destroy(EVP_MD_CTX *); const EVP_MD *EVP_get_digestbyname(const char *); -const EVP_MD *EVP_MD_CTX_md(const EVP_MD_CTX *); -int EVP_MD_size(const EVP_MD *); EVP_PKEY *EVP_PKEY_new(void); void EVP_PKEY_free(EVP_PKEY *); int EVP_PKEY_type(int); int EVP_PKEY_bits(EVP_PKEY *); +int EVP_PKEY_size(EVP_PKEY *); RSA *EVP_PKEY_get1_RSA(EVP_PKEY *); int EVP_SignInit(EVP_MD_CTX *, const EVP_MD *); @@ -104,6 +101,19 @@ int PKCS5_PBKDF2_HMAC_SHA1(const char *, int, const unsigned char *, int, int, int EVP_PKEY_set1_RSA(EVP_PKEY *, struct rsa_st *); int EVP_PKEY_set1_DSA(EVP_PKEY *, struct dsa_st *); + +int EVP_PKEY_get_attr_count(const EVP_PKEY *); +int EVP_PKEY_get_attr_by_NID(const EVP_PKEY *, int, int); +int EVP_PKEY_get_attr_by_OBJ(const EVP_PKEY *, ASN1_OBJECT *, int); +X509_ATTRIBUTE *EVP_PKEY_get_attr(const EVP_PKEY *, int); +X509_ATTRIBUTE *EVP_PKEY_delete_attr(EVP_PKEY *, int); +int EVP_PKEY_add1_attr(EVP_PKEY *, X509_ATTRIBUTE *); +int EVP_PKEY_add1_attr_by_OBJ(EVP_PKEY *, const ASN1_OBJECT *, int, + const unsigned char *, int); +int EVP_PKEY_add1_attr_by_NID(EVP_PKEY *, int, int, + const unsigned char *, int); +int EVP_PKEY_add1_attr_by_txt(EVP_PKEY *, const char *, int, + const unsigned char *, int); """ MACROS = """ @@ -129,6 +139,13 @@ int EVP_PKEY_sign(EVP_PKEY_CTX *, unsigned char *, size_t *, int EVP_PKEY_verify_init(EVP_PKEY_CTX *); int EVP_PKEY_verify(EVP_PKEY_CTX *, const unsigned char *, size_t, const unsigned char *, size_t); + +/* The following were macros in 0.9.8e. Once we drop support for RHEL/CentOS 5 + we should move these back to FUNCTIONS. */ +const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *); +int EVP_CIPHER_block_size(const EVP_CIPHER *); +const EVP_MD *EVP_MD_CTX_md(const EVP_MD_CTX *); +int EVP_MD_size(const EVP_MD *); """ CUSTOMIZATIONS = """ @@ -140,7 +157,7 @@ const long EVP_CTRL_GCM_GET_TAG = -1; const long EVP_CTRL_GCM_SET_TAG = -1; const long EVP_CTRL_GCM_SET_IVLEN = -1; #endif -#if OPENSSL_VERSION_NUMBER >= 0x10000000 +#if OPENSSL_VERSION_NUMBER >= 0x10000000L const long Cryptography_HAS_PBKDF2_HMAC = 1; const long Cryptography_HAS_PKEY_CTX = 1; #else diff --git a/cryptography/hazmat/bindings/openssl/hmac.py b/cryptography/hazmat/bindings/openssl/hmac.py index 5f9e0945..4b81c9df 100644 --- a/cryptography/hazmat/bindings/openssl/hmac.py +++ b/cryptography/hazmat/bindings/openssl/hmac.py @@ -55,11 +55,11 @@ int Cryptography_HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, } int Cryptography_HMAC_Final(HMAC_CTX *ctx, unsigned char *digest, - unsigned int *digest_len) { + unsigned int *outlen) { #if OPENSSL_VERSION_NUMBER >= 0x010000000 - return HMAC_Final(ctx, digest, digest_len); + return HMAC_Final(ctx, digest, outlen); #else - HMAC_Final(ctx, digest, digest_len); + HMAC_Final(ctx, digest, outlen); return 1; #endif } diff --git a/cryptography/hazmat/bindings/openssl/nid.py b/cryptography/hazmat/bindings/openssl/nid.py index 40aed19f..cb83c1ba 100644 --- a/cryptography/hazmat/bindings/openssl/nid.py +++ b/cryptography/hazmat/bindings/openssl/nid.py @@ -14,6 +14,8 @@ INCLUDES = "" TYPES = """ +static const int Cryptography_HAS_ECDSA_SHA2_NIDS; + static const int NID_undef; static const int NID_dsa; static const int NID_dsaWithSHA; @@ -38,6 +40,148 @@ static const int NID_ecdsa_with_SHA512; static const int NID_crl_reason; static const int NID_pbe_WithSHA1And3_Key_TripleDES_CBC; static const int NID_subject_alt_name; +static const int NID_X9_62_c2pnb163v1; +static const int NID_X9_62_c2pnb163v2; +static const int NID_X9_62_c2pnb163v3; +static const int NID_X9_62_c2pnb176v1; +static const int NID_X9_62_c2tnb191v1; +static const int NID_X9_62_c2tnb191v2; +static const int NID_X9_62_c2tnb191v3; +static const int NID_X9_62_c2onb191v4; +static const int NID_X9_62_c2onb191v5; +static const int NID_X9_62_c2pnb208w1; +static const int NID_X9_62_c2tnb239v1; +static const int NID_X9_62_c2tnb239v2; +static const int NID_X9_62_c2tnb239v3; +static const int NID_X9_62_c2onb239v4; +static const int NID_X9_62_c2onb239v5; +static const int NID_X9_62_c2pnb272w1; +static const int NID_X9_62_c2pnb304w1; +static const int NID_X9_62_c2tnb359v1; +static const int NID_X9_62_c2pnb368w1; +static const int NID_X9_62_c2tnb431r1; +static const int NID_X9_62_prime192v1; +static const int NID_X9_62_prime192v2; +static const int NID_X9_62_prime192v3; +static const int NID_X9_62_prime239v1; +static const int NID_X9_62_prime239v2; +static const int NID_X9_62_prime239v3; +static const int NID_X9_62_prime256v1; +static const int NID_secp112r1; +static const int NID_secp112r2; +static const int NID_secp128r1; +static const int NID_secp128r2; +static const int NID_secp160k1; +static const int NID_secp160r1; +static const int NID_secp160r2; +static const int NID_sect163k1; +static const int NID_sect163r1; +static const int NID_sect163r2; +static const int NID_secp192k1; +static const int NID_secp224k1; +static const int NID_secp224r1; +static const int NID_secp256k1; +static const int NID_secp384r1; +static const int NID_secp521r1; +static const int NID_sect113r1; +static const int NID_sect113r2; +static const int NID_sect131r1; +static const int NID_sect131r2; +static const int NID_sect193r1; +static const int NID_sect193r2; +static const int NID_sect233k1; +static const int NID_sect233r1; +static const int NID_sect239k1; +static const int NID_sect283k1; +static const int NID_sect283r1; +static const int NID_sect409k1; +static const int NID_sect409r1; +static const int NID_sect571k1; +static const int NID_sect571r1; +static const int NID_wap_wsg_idm_ecid_wtls1; +static const int NID_wap_wsg_idm_ecid_wtls3; +static const int NID_wap_wsg_idm_ecid_wtls4; +static const int NID_wap_wsg_idm_ecid_wtls5; +static const int NID_wap_wsg_idm_ecid_wtls6; +static const int NID_wap_wsg_idm_ecid_wtls7; +static const int NID_wap_wsg_idm_ecid_wtls8; +static const int NID_wap_wsg_idm_ecid_wtls9; +static const int NID_wap_wsg_idm_ecid_wtls10; +static const int NID_wap_wsg_idm_ecid_wtls11; +static const int NID_wap_wsg_idm_ecid_wtls12; +static const int NID_ipsec3; +static const int NID_ipsec4; +static const char *const SN_X9_62_c2pnb163v1; +static const char *const SN_X9_62_c2pnb163v2; +static const char *const SN_X9_62_c2pnb163v3; +static const char *const SN_X9_62_c2pnb176v1; +static const char *const SN_X9_62_c2tnb191v1; +static const char *const SN_X9_62_c2tnb191v2; +static const char *const SN_X9_62_c2tnb191v3; +static const char *const SN_X9_62_c2onb191v4; +static const char *const SN_X9_62_c2onb191v5; +static const char *const SN_X9_62_c2pnb208w1; +static const char *const SN_X9_62_c2tnb239v1; +static const char *const SN_X9_62_c2tnb239v2; +static const char *const SN_X9_62_c2tnb239v3; +static const char *const SN_X9_62_c2onb239v4; +static const char *const SN_X9_62_c2onb239v5; +static const char *const SN_X9_62_c2pnb272w1; +static const char *const SN_X9_62_c2pnb304w1; +static const char *const SN_X9_62_c2tnb359v1; +static const char *const SN_X9_62_c2pnb368w1; +static const char *const SN_X9_62_c2tnb431r1; +static const char *const SN_X9_62_prime192v1; +static const char *const SN_X9_62_prime192v2; +static const char *const SN_X9_62_prime192v3; +static const char *const SN_X9_62_prime239v1; +static const char *const SN_X9_62_prime239v2; +static const char *const SN_X9_62_prime239v3; +static const char *const SN_X9_62_prime256v1; +static const char *const SN_secp112r1; +static const char *const SN_secp112r2; +static const char *const SN_secp128r1; +static const char *const SN_secp128r2; +static const char *const SN_secp160k1; +static const char *const SN_secp160r1; +static const char *const SN_secp160r2; +static const char *const SN_sect163k1; +static const char *const SN_sect163r1; +static const char *const SN_sect163r2; +static const char *const SN_secp192k1; +static const char *const SN_secp224k1; +static const char *const SN_secp224r1; +static const char *const SN_secp256k1; +static const char *const SN_secp384r1; +static const char *const SN_secp521r1; +static const char *const SN_sect113r1; +static const char *const SN_sect113r2; +static const char *const SN_sect131r1; +static const char *const SN_sect131r2; +static const char *const SN_sect193r1; +static const char *const SN_sect193r2; +static const char *const SN_sect233k1; +static const char *const SN_sect233r1; +static const char *const SN_sect239k1; +static const char *const SN_sect283k1; +static const char *const SN_sect283r1; +static const char *const SN_sect409k1; +static const char *const SN_sect409r1; +static const char *const SN_sect571k1; +static const char *const SN_sect571r1; +static const char *const SN_wap_wsg_idm_ecid_wtls1; +static const char *const SN_wap_wsg_idm_ecid_wtls3; +static const char *const SN_wap_wsg_idm_ecid_wtls4; +static const char *const SN_wap_wsg_idm_ecid_wtls5; +static const char *const SN_wap_wsg_idm_ecid_wtls6; +static const char *const SN_wap_wsg_idm_ecid_wtls7; +static const char *const SN_wap_wsg_idm_ecid_wtls8; +static const char *const SN_wap_wsg_idm_ecid_wtls9; +static const char *const SN_wap_wsg_idm_ecid_wtls10; +static const char *const SN_wap_wsg_idm_ecid_wtls11; +static const char *const SN_wap_wsg_idm_ecid_wtls12; +static const char *const SN_ipsec3; +static const char *const SN_ipsec4; """ FUNCTIONS = """ @@ -47,6 +191,23 @@ MACROS = """ """ CUSTOMIZATIONS = """ +// OpenSSL 0.9.8g+ +#if OPENSSL_VERSION_NUMBER >= 0x0090807fL +static const long Cryptography_HAS_ECDSA_SHA2_NIDS = 1; +#else +static const long Cryptography_HAS_ECDSA_SHA2_NIDS = 0; +static const int NID_ecdsa_with_SHA224 = 0; +static const int NID_ecdsa_with_SHA256 = 0; +static const int NID_ecdsa_with_SHA384 = 0; +static const int NID_ecdsa_with_SHA512 = 0; +#endif """ -CONDITIONAL_NAMES = {} +CONDITIONAL_NAMES = { + "Cryptography_HAS_ECDSA_SHA2_NIDS": [ + "NID_ecdsa_with_SHA224", + "NID_ecdsa_with_SHA256", + "NID_ecdsa_with_SHA384", + "NID_ecdsa_with_SHA512", + ], +} diff --git a/cryptography/hazmat/bindings/openssl/osrandom_engine.py b/cryptography/hazmat/bindings/openssl/osrandom_engine.py index 23f2e17a..0903a4bf 100644 --- a/cryptography/hazmat/bindings/openssl/osrandom_engine.py +++ b/cryptography/hazmat/bindings/openssl/osrandom_engine.py @@ -174,8 +174,19 @@ static RAND_METHOD osrandom_rand = { osrandom_rand_status, }; +/* Returns 1 if successfully added, 2 if engine has previously been added, + and 0 for error. */ int Cryptography_add_osrandom_engine(void) { - ENGINE *e = ENGINE_new(); + ENGINE *e; + e = ENGINE_by_id(Cryptography_osrandom_engine_id); + if (e != NULL) { + ENGINE_free(e); + return 2; + } else { + ERR_clear_error(); + } + + e = ENGINE_new(); if (e == NULL) { return 0; } diff --git a/cryptography/hazmat/bindings/openssl/pem.py b/cryptography/hazmat/bindings/openssl/pem.py index 8b717c2d..942cba34 100644 --- a/cryptography/hazmat/bindings/openssl/pem.py +++ b/cryptography/hazmat/bindings/openssl/pem.py @@ -31,9 +31,13 @@ EVP_PKEY *PEM_read_bio_PrivateKey(BIO *, EVP_PKEY **, pem_password_cb *, int PEM_write_bio_PKCS8PrivateKey(BIO *, EVP_PKEY *, const EVP_CIPHER *, char *, int, pem_password_cb *, void *); +int PEM_write_bio_PKCS8PrivateKey_nid(BIO *, EVP_PKEY *, int, char *, int, + pem_password_cb *, void *); int i2d_PKCS8PrivateKey_bio(BIO *, EVP_PKEY *, const EVP_CIPHER *, char *, int, pem_password_cb *, void *); +int i2d_PKCS8PrivateKey_nid_bio(BIO *, EVP_PKEY *, int, + char *, int, pem_password_cb *, void *); EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *, EVP_PKEY **, pem_password_cb *, void *); diff --git a/cryptography/hazmat/bindings/openssl/rsa.py b/cryptography/hazmat/bindings/openssl/rsa.py index b6f7d04c..f895cd02 100644 --- a/cryptography/hazmat/bindings/openssl/rsa.py +++ b/cryptography/hazmat/bindings/openssl/rsa.py @@ -33,7 +33,11 @@ static const int RSA_SSLV23_PADDING; static const int RSA_NO_PADDING; static const int RSA_PKCS1_OAEP_PADDING; static const int RSA_X931_PADDING; +static const int RSA_PKCS1_PSS_PADDING; static const int RSA_F4; + +static const int Cryptography_HAS_PSS_PADDING; +static const int Cryptography_HAS_MGF1_MD; """ FUNCTIONS = """ @@ -67,13 +71,24 @@ int RSA_padding_check_PKCS1_OAEP(unsigned char *, int, const unsigned char *, MACROS = """ int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *, int); int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *, int); +int EVP_PKEY_CTX_set_rsa_mgf1_md(EVP_PKEY_CTX *, EVP_MD *); """ CUSTOMIZATIONS = """ -#if OPENSSL_VERSION_NUMBER < 0x10000000 +#if OPENSSL_VERSION_NUMBER >= 0x10000000 +static const long Cryptography_HAS_PSS_PADDING = 1; +#else // see evp.py for the definition of Cryptography_HAS_PKEY_CTX +static const long Cryptography_HAS_PSS_PADDING = 0; int (*EVP_PKEY_CTX_set_rsa_padding)(EVP_PKEY_CTX *, int) = NULL; int (*EVP_PKEY_CTX_set_rsa_pss_saltlen)(EVP_PKEY_CTX *, int) = NULL; +static const long RSA_PKCS1_PSS_PADDING = 0; +#endif +#if OPENSSL_VERSION_NUMBER >= 0x1000100f +static const long Cryptography_HAS_MGF1_MD = 1; +#else +static const long Cryptography_HAS_MGF1_MD = 0; +int (*EVP_PKEY_CTX_set_rsa_mgf1_md)(EVP_PKEY_CTX *, EVP_MD *) = NULL; #endif """ @@ -81,5 +96,11 @@ CONDITIONAL_NAMES = { "Cryptography_HAS_PKEY_CTX": [ "EVP_PKEY_CTX_set_rsa_padding", "EVP_PKEY_CTX_set_rsa_pss_saltlen", - ] + ], + "Cryptography_HAS_PSS_PADDING": [ + "RSA_PKCS1_PSS_PADDING", + ], + "Cryptography_HAS_MGF1_MD": [ + "EVP_PKEY_CTX_set_rsa_mgf1_md", + ], } diff --git a/cryptography/hazmat/bindings/openssl/ssl.py b/cryptography/hazmat/bindings/openssl/ssl.py index 038ea54b..25bef49a 100644 --- a/cryptography/hazmat/bindings/openssl/ssl.py +++ b/cryptography/hazmat/bindings/openssl/ssl.py @@ -37,6 +37,8 @@ static const int Cryptography_HAS_RELEASE_BUFFERS; static const int Cryptography_HAS_OP_NO_COMPRESSION; static const int Cryptography_HAS_SSL_OP_MSIE_SSLV2_RSA_PADDING; +static const int Cryptography_HAS_SSL_SET_SSL_CTX; +static const int Cryptography_HAS_SSL_OP_NO_TICKET; static const int SSL_FILETYPE_PEM; static const int SSL_FILETYPE_ASN1; @@ -136,10 +138,13 @@ typedef struct { typedef struct { SSL3_STATE *s3; SSL_SESSION *session; + int type; ...; } SSL; static const int TLSEXT_NAMETYPE_host_name; + +typedef ... SSL_CIPHER; """ FUNCTIONS = """ @@ -147,7 +152,6 @@ void SSL_load_error_strings(void); int SSL_library_init(void); /* SSL */ -SSL_CTX *SSL_set_SSL_CTX(SSL *, SSL_CTX *); SSL_SESSION *SSL_get1_session(SSL *); int SSL_set_session(SSL *, SSL_SESSION *); int SSL_get_verify_mode(const SSL *); @@ -184,8 +188,6 @@ int SSL_CTX_set_default_verify_paths(SSL_CTX *); void SSL_CTX_set_verify(SSL_CTX *, int, int (*)(int, X509_STORE_CTX *)); void SSL_CTX_set_verify_depth(SSL_CTX *, int); int (*SSL_CTX_get_verify_callback(const SSL_CTX *))(int, X509_STORE_CTX *); -void SSL_CTX_set_info_callback(SSL_CTX *, void (*)(const SSL *, int, int)); -void (*SSL_CTX_get_info_callback(SSL_CTX *))(const SSL *, int, int); int SSL_CTX_get_verify_mode(const SSL_CTX *); int SSL_CTX_get_verify_depth(const SSL_CTX *); int SSL_CTX_set_cipher_list(SSL_CTX *, const char *); @@ -212,6 +214,14 @@ X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *); /* SSL_SESSION */ void SSL_SESSION_free(SSL_SESSION *); + +/* Information about actually used cipher */ +const char *SSL_CIPHER_get_name(const SSL_CIPHER *); +int SSL_CIPHER_get_bits(const SSL_CIPHER *, int *); +char *SSL_CIPHER_get_version(const SSL_CIPHER *); + +size_t SSL_get_finished(const SSL *, void *, size_t); +size_t SSL_get_peer_finished(const SSL *, void *, size_t); """ MACROS = """ @@ -281,6 +291,8 @@ const SSL_METHOD *SSLv23_client_method(void); SSL_CTX *SSL_CTX_new(SSL_METHOD *); long SSL_CTX_get_timeout(const SSL_CTX *); +const SSL_CIPHER *SSL_get_current_cipher(const SSL *); + /* SNI APIs were introduced in OpenSSL 1.0.0. To continue to support * earlier versions some special handling of these is necessary. */ @@ -289,6 +301,16 @@ void SSL_set_tlsext_host_name(SSL *, char *); void SSL_CTX_set_tlsext_servername_callback( SSL_CTX *, int (*)(const SSL *, int *, void *)); + +long SSL_session_reused(SSL *); + +/* The following were macros in 0.9.8e. Once we drop support for RHEL/CentOS 5 + we should move these back to FUNCTIONS. */ +void SSL_CTX_set_info_callback(SSL_CTX *, void (*)(const SSL *, int, int)); +void (*SSL_CTX_get_info_callback(SSL_CTX *))(const SSL *, int, int); +/* This function does not exist in 0.9.8e. Once we drop support for + RHEL/CentOS 5 this can be moved back to FUNCTIONS. */ +SSL_CTX *SSL_set_SSL_CTX(SSL *, SSL_CTX *); """ CUSTOMIZATIONS = """ @@ -356,6 +378,22 @@ const long SSL_OP_MSIE_SSLV2_RSA_PADDING = 0; #ifdef OPENSSL_NO_EC long (*SSL_CTX_set_tmp_ecdh)(SSL_CTX *, EC_KEY *) = NULL; #endif + +#ifdef SSL_OP_NO_TICKET +static const long Cryptography_HAS_SSL_OP_NO_TICKET = 1; +#else +static const long Cryptography_HAS_SSL_OP_NO_TICKET = 0; +const long SSL_OP_NO_TICKET = 0; +#endif + +// OpenSSL 0.9.8f+ +#if OPENSSL_VERSION_NUMBER >= 0x00908070L +static const long Cryptography_HAS_SSL_SET_SSL_CTX = 1; +#else +static const long Cryptography_HAS_SSL_SET_SSL_CTX = 0; +static const int TLSEXT_NAMETYPE_host_name = 0; +SSL_CTX *(*SSL_set_SSL_CTX)(SSL *, SSL_CTX *) = NULL; +#endif """ CONDITIONAL_NAMES = { @@ -399,5 +437,14 @@ CONDITIONAL_NAMES = { "Cryptography_HAS_EC": [ "SSL_CTX_set_tmp_ecdh", - ] + ], + + "Cryptography_HAS_SSL_OP_NO_TICKET": [ + "SSL_OP_NO_TICKET", + ], + + "Cryptography_HAS_SSL_SET_SSL_CTX": [ + "SSL_set_SSL_CTX", + "TLSEXT_NAMETYPE_host_name", + ], } diff --git a/cryptography/hazmat/bindings/openssl/x509.py b/cryptography/hazmat/bindings/openssl/x509.py index 74259b3d..e8b036c3 100644 --- a/cryptography/hazmat/bindings/openssl/x509.py +++ b/cryptography/hazmat/bindings/openssl/x509.py @@ -34,6 +34,8 @@ typedef struct { ...; } X509_ALGOR; +typedef ... X509_ATTRIBUTE; + typedef struct { X509_ALGOR *signature; ...; @@ -118,8 +120,6 @@ int X509_REQ_set_pubkey(X509_REQ *, EVP_PKEY *); int X509_REQ_sign(X509_REQ *, EVP_PKEY *, const EVP_MD *); int X509_REQ_verify(X509_REQ *, EVP_PKEY *); EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *); -int X509_REQ_add_extensions(X509_REQ *, X509_EXTENSIONS *); -X509_EXTENSIONS *X509_REQ_get_extensions(X509_REQ *); int X509_REQ_print_ex(BIO *, X509_REQ *, unsigned long, unsigned long); int X509V3_EXT_print(BIO *, X509_EXTENSION *, unsigned long, int); @@ -169,6 +169,13 @@ int X509_STORE_add_cert(X509_STORE *, X509 *); int X509_verify_cert(X509_STORE_CTX *); const char *X509_verify_cert_error_string(long); + +const char *X509_get_default_cert_area(void); +const char *X509_get_default_cert_dir(void); +const char *X509_get_default_cert_file(void); +const char *X509_get_default_cert_dir_env(void); +const char *X509_get_default_cert_file_env(void); +const char *X509_get_default_private_dir(void); """ MACROS = """ @@ -199,9 +206,18 @@ X509_REVOKED *sk_X509_REVOKED_value(Cryptography_STACK_OF_X509_REVOKED *, int); /* These aren't macros these arguments are all const X on openssl > 1.0.x */ int X509_CRL_set_lastUpdate(X509_CRL *, ASN1_TIME *); int X509_CRL_set_nextUpdate(X509_CRL *, ASN1_TIME *); + +/* these use STACK_OF(X509_EXTENSION) in 0.9.8e. Once we drop support for + RHEL/CentOS 5 we should move these back to FUNCTIONS. */ +int X509_REQ_add_extensions(X509_REQ *, X509_EXTENSIONS *); +X509_EXTENSIONS *X509_REQ_get_extensions(X509_REQ *); """ CUSTOMIZATIONS = """ +// OpenSSL 0.9.8e does not have this definition +#if OPENSSL_VERSION_NUMBER <= 0x0090805fL +typedef STACK_OF(X509_EXTENSION) X509_EXTENSIONS; +#endif """ CONDITIONAL_NAMES = {} diff --git a/cryptography/hazmat/primitives/asymmetric/padding.py b/cryptography/hazmat/primitives/asymmetric/padding.py new file mode 100644 index 00000000..6bafe314 --- /dev/null +++ b/cryptography/hazmat/primitives/asymmetric/padding.py @@ -0,0 +1,22 @@ +# 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 import utils +from cryptography.hazmat.primitives import interfaces + + +@utils.register_interface(interfaces.AsymmetricPadding) +class PKCS1v15(object): + name = "EMSA-PKCS1-v1_5" diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py index 01218592..dfb43340 100644 --- a/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -13,21 +13,12 @@ from __future__ import absolute_import, division, print_function -import sys - import six from cryptography import utils from cryptography.hazmat.primitives import interfaces -def _bit_length(x): - if sys.version_info >= (2, 7): - return x.bit_length() - else: - return len(bin(x)) - (2 + (x <= 0)) - - @utils.register_interface(interfaces.RSAPublicKey) class RSAPublicKey(object): def __init__(self, public_exponent, modulus): @@ -49,9 +40,13 @@ class RSAPublicKey(object): self._public_exponent = public_exponent self._modulus = modulus + def verifier(self, signature, padding, algorithm, backend): + return backend.create_rsa_verification_ctx(self, signature, padding, + algorithm) + @property def key_size(self): - return _bit_length(self.modulus) + return utils.bit_length(self.modulus) @property def public_exponent(self): @@ -132,12 +127,15 @@ class RSAPrivateKey(object): self._modulus = modulus @classmethod - def generate(self, public_exponent, key_size, backend): + def generate(cls, public_exponent, key_size, backend): return backend.generate_rsa_private_key(public_exponent, key_size) + def signer(self, padding, algorithm, backend): + return backend.create_rsa_signature_ctx(self, padding, algorithm) + @property def key_size(self): - return _bit_length(self.modulus) + return utils.bit_length(self.modulus) def public_key(self): return RSAPublicKey(self.public_exponent, self.modulus) diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py index 5ef469d0..3824bcde 100644 --- a/cryptography/hazmat/primitives/interfaces.py +++ b/cryptography/hazmat/primitives/interfaces.py @@ -287,6 +287,135 @@ class RSAPublicKey(six.with_metaclass(abc.ABCMeta)): """ +class DSAParameters(six.with_metaclass(abc.ABCMeta)): + @abc.abstractproperty + def modulus(self): + """ + The prime modulus that's used in generating the DSA keypair and used + in the DSA signing and verification processes. + """ + + @abc.abstractproperty + def subgroup_order(self): + """ + The subgroup order that's used in generating the DSA keypair + by the generator and used in the DSA signing and verification + processes. + """ + + @abc.abstractproperty + def generator(self): + """ + The generator that is used in generating the DSA keypair and used + in the DSA signing and verification processes. + """ + + @abc.abstractproperty + def p(self): + """ + The prime modulus that's used in generating the DSA keypair and used + in the DSA signing and verification processes. Alias for modulus. + """ + + @abc.abstractproperty + def q(self): + """ + The subgroup order that's used in generating the DSA keypair + by the generator and used in the DSA signing and verification + processes. Alias for subgroup_order. + """ + + @abc.abstractproperty + def g(self): + """ + The generator that is used in generating the DSA keypair and used + in the DSA signing and verification processes. Alias for generator. + """ + + +class DSAPrivateKey(six.with_metaclass(abc.ABCMeta)): + @abc.abstractproperty + def key_size(self): + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def public_key(self): + """ + The DSAPublicKey associated with this private key. + """ + + @abc.abstractproperty + def x(self): + """ + The private key "x" in the DSA structure. + """ + + @abc.abstractproperty + def y(self): + """ + The public key. + """ + + @abc.abstractmethod + def parameters(self): + """ + The DSAParameters object associated with this private key. + """ + + +class DSAPublicKey(six.with_metaclass(abc.ABCMeta)): + @abc.abstractproperty + def y(self): + """ + The public key. + """ + + @abc.abstractmethod + def parameters(self): + """ + The DSAParameters object associated with this public key. + """ + + +class AsymmetricSignatureContext(six.with_metaclass(abc.ABCMeta)): + @abc.abstractmethod + def update(self, data): + """ + Processes the provided bytes and returns nothing. + """ + + @abc.abstractmethod + def finalize(self): + """ + Returns the signature as bytes. + """ + + +class AsymmetricVerificationContext(six.with_metaclass(abc.ABCMeta)): + @abc.abstractmethod + def update(self, data): + """ + Processes the provided bytes and returns nothing. + """ + + @abc.abstractmethod + def verify(self): + """ + Raises an exception if the bytes provided to update do not match the + signature or the signature does not match the public key. + """ + + +class AsymmetricPadding(six.with_metaclass(abc.ABCMeta)): + @abc.abstractproperty + def name(self): + """ + A string naming this padding (e.g. "PSS", "PKCS1"). + """ + + class KeyDerivationFunction(six.with_metaclass(abc.ABCMeta)): @abc.abstractmethod def derive(self, key_material): diff --git a/cryptography/hazmat/primitives/kdf/pbkdf2.py b/cryptography/hazmat/primitives/kdf/pbkdf2.py index 71b88211..39427780 100644 --- a/cryptography/hazmat/primitives/kdf/pbkdf2.py +++ b/cryptography/hazmat/primitives/kdf/pbkdf2.py @@ -17,7 +17,7 @@ import six from cryptography import utils from cryptography.exceptions import ( - InvalidKey, UnsupportedAlgorithm, AlreadyFinalized + InvalidKey, UnsupportedHash, AlreadyFinalized ) from cryptography.hazmat.primitives import constant_time, interfaces @@ -26,7 +26,7 @@ from cryptography.hazmat.primitives import constant_time, interfaces class PBKDF2HMAC(object): def __init__(self, algorithm, length, salt, iterations, backend): if not backend.pbkdf2_hmac_supported(algorithm): - raise UnsupportedAlgorithm( + raise UnsupportedHash( "{0} is not supported for PBKDF2 by this backend".format( algorithm.name) ) diff --git a/cryptography/hazmat/primitives/twofactor/__init__.py b/cryptography/hazmat/primitives/twofactor/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/cryptography/hazmat/primitives/twofactor/__init__.py diff --git a/cryptography/hazmat/primitives/twofactor/hotp.py b/cryptography/hazmat/primitives/twofactor/hotp.py new file mode 100644 index 00000000..83260225 --- /dev/null +++ b/cryptography/hazmat/primitives/twofactor/hotp.py @@ -0,0 +1,62 @@ +# 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 struct + +import six + +from cryptography.exceptions import InvalidToken +from cryptography.hazmat.primitives import constant_time, hmac +from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512 + + +class HOTP(object): + def __init__(self, key, length, algorithm, backend): + if len(key) < 16: + raise ValueError("Key length has to be at least 128 bits.") + + if not isinstance(length, six.integer_types): + raise TypeError("Length parameter must be an integer type") + + if length < 6 or length > 8: + raise ValueError("Length of HOTP has to be between 6 to 8.") + + if not isinstance(algorithm, (SHA1, SHA256, SHA512)): + raise TypeError("Algorithm must be SHA1, SHA256 or SHA512") + + self._key = key + self._length = length + self._algorithm = algorithm + self._backend = backend + + def generate(self, counter): + truncated_value = self._dynamic_truncate(counter) + hotp = truncated_value % (10 ** self._length) + return "{0:0{1}}".format(hotp, self._length).encode() + + def verify(self, hotp, counter): + if not constant_time.bytes_eq(self.generate(counter), hotp): + raise InvalidToken("Supplied HOTP value does not match") + + def _dynamic_truncate(self, counter): + ctx = hmac.HMAC(self._key, self._algorithm, self._backend) + ctx.update(struct.pack(">Q", counter)) + hmac_value = ctx.finalize() + + offset_bits = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111 + + offset = int(offset_bits) + p = hmac_value[offset:offset + 4] + return struct.unpack(">I", p)[0] & 0x7fffffff diff --git a/cryptography/hazmat/primitives/twofactor/totp.py b/cryptography/hazmat/primitives/twofactor/totp.py new file mode 100644 index 00000000..0630de69 --- /dev/null +++ b/cryptography/hazmat/primitives/twofactor/totp.py @@ -0,0 +1,32 @@ +# 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 InvalidToken +from cryptography.hazmat.primitives import constant_time +from cryptography.hazmat.primitives.twofactor.hotp import HOTP + + +class TOTP(object): + def __init__(self, key, length, algorithm, time_step, backend): + self._time_step = time_step + self._hotp = HOTP(key, length, algorithm, backend) + + def generate(self, time): + counter = int(time / self._time_step) + return self._hotp.generate(counter) + + def verify(self, totp, time): + if not constant_time.bytes_eq(self.generate(time), totp): + raise InvalidToken("Supplied TOTP value does not match") diff --git a/cryptography/utils.py b/cryptography/utils.py index e697d515..eac833b6 100644 --- a/cryptography/utils.py +++ b/cryptography/utils.py @@ -13,9 +13,18 @@ from __future__ import absolute_import, division, print_function +import sys + def register_interface(iface): def register_decorator(klass): iface.register(klass) return klass return register_decorator + + +def bit_length(x): + if sys.version_info >= (2, 7): + return x.bit_length() + else: + return len(bin(x)) - (2 + (x <= 0)) |