diff options
Diffstat (limited to 'src')
96 files changed, 12481 insertions, 0 deletions
diff --git a/src/cryptography/__about__.py b/src/cryptography/__about__.py new file mode 100644 index 00000000..3d39d18b --- /dev/null +++ b/src/cryptography/__about__.py @@ -0,0 +1,31 @@ +# 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 + +__all__ = [ +    "__title__", "__summary__", "__uri__", "__version__", "__author__", +    "__email__", "__license__", "__copyright__", +] + +__title__ = "cryptography" +__summary__ = ("cryptography is a package which provides cryptographic recipes" +               " and primitives to Python developers.") +__uri__ = "https://github.com/pyca/cryptography" + +__version__ = "0.7.dev1" + +__author__ = "The cryptography developers" +__email__ = "cryptography-dev@python.org" + +__license__ = "BSD or Apache License, Version 2.0" +__copyright__ = "Copyright 2013-2014 {0}".format(__author__) diff --git a/src/cryptography/__init__.py b/src/cryptography/__init__.py new file mode 100644 index 00000000..f27ba856 --- /dev/null +++ b/src/cryptography/__init__.py @@ -0,0 +1,25 @@ +# 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.__about__ import ( +    __author__, __copyright__, __email__, __license__, __summary__, __title__, +    __uri__, __version__ +) + + +__all__ = [ +    "__title__", "__summary__", "__uri__", "__version__", "__author__", +    "__email__", "__license__", "__copyright__", +] diff --git a/src/cryptography/exceptions.py b/src/cryptography/exceptions.py new file mode 100644 index 00000000..c14763f7 --- /dev/null +++ b/src/cryptography/exceptions.py @@ -0,0 +1,63 @@ +# 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 + + +class _Reasons(object): +    BACKEND_MISSING_INTERFACE = object() +    UNSUPPORTED_HASH = object() +    UNSUPPORTED_CIPHER = object() +    UNSUPPORTED_PADDING = object() +    UNSUPPORTED_MGF = object() +    UNSUPPORTED_PUBLIC_KEY_ALGORITHM = object() +    UNSUPPORTED_ELLIPTIC_CURVE = object() +    UNSUPPORTED_SERIALIZATION = object() + + +class UnsupportedAlgorithm(Exception): +    def __init__(self, message, reason=None): +        super(UnsupportedAlgorithm, self).__init__(message) +        self._reason = reason + + +class AlreadyFinalized(Exception): +    pass + + +class AlreadyUpdated(Exception): +    pass + + +class NotYetFinalized(Exception): +    pass + + +class InvalidTag(Exception): +    pass + + +class InvalidSignature(Exception): +    pass + + +class InternalError(Exception): +    pass + + +class InvalidKey(Exception): +    pass + + +class InvalidToken(Exception): +    pass diff --git a/src/cryptography/fernet.py b/src/cryptography/fernet.py new file mode 100644 index 00000000..4f98feec --- /dev/null +++ b/src/cryptography/fernet.py @@ -0,0 +1,150 @@ +# 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 base64 +import binascii +import os +import struct +import time + +import six + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, padding +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.hmac import HMAC + + +class InvalidToken(Exception): +    pass + + +_MAX_CLOCK_SKEW = 60 + + +class Fernet(object): +    def __init__(self, key, backend=None): +        if backend is None: +            backend = default_backend() + +        key = base64.urlsafe_b64decode(key) +        if len(key) != 32: +            raise ValueError( +                "Fernet key must be 32 url-safe base64-encoded bytes." +            ) + +        self._signing_key = key[:16] +        self._encryption_key = key[16:] +        self._backend = backend + +    @classmethod +    def generate_key(cls): +        return base64.urlsafe_b64encode(os.urandom(32)) + +    def encrypt(self, data): +        current_time = int(time.time()) +        iv = os.urandom(16) +        return self._encrypt_from_parts(data, current_time, iv) + +    def _encrypt_from_parts(self, data, current_time, iv): +        if not isinstance(data, bytes): +            raise TypeError("data must be bytes.") + +        padder = padding.PKCS7(algorithms.AES.block_size).padder() +        padded_data = padder.update(data) + padder.finalize() +        encryptor = Cipher( +            algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend +        ).encryptor() +        ciphertext = encryptor.update(padded_data) + encryptor.finalize() + +        basic_parts = ( +            b"\x80" + struct.pack(">Q", current_time) + iv + ciphertext +        ) + +        h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) +        h.update(basic_parts) +        hmac = h.finalize() +        return base64.urlsafe_b64encode(basic_parts + hmac) + +    def decrypt(self, token, ttl=None): +        if not isinstance(token, bytes): +            raise TypeError("token must be bytes.") + +        current_time = int(time.time()) + +        try: +            data = base64.urlsafe_b64decode(token) +        except (TypeError, binascii.Error): +            raise InvalidToken + +        if not data or six.indexbytes(data, 0) != 0x80: +            raise InvalidToken + +        try: +            timestamp, = struct.unpack(">Q", data[1:9]) +        except struct.error: +            raise InvalidToken +        if ttl is not None: +            if timestamp + ttl < current_time: +                raise InvalidToken +        if current_time + _MAX_CLOCK_SKEW < timestamp: +            raise InvalidToken +        h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) +        h.update(data[:-32]) +        try: +            h.verify(data[-32:]) +        except InvalidSignature: +            raise InvalidToken + +        iv = data[9:25] +        ciphertext = data[25:-32] +        decryptor = Cipher( +            algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend +        ).decryptor() +        plaintext_padded = decryptor.update(ciphertext) +        try: +            plaintext_padded += decryptor.finalize() +        except ValueError: +            raise InvalidToken +        unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() + +        unpadded = unpadder.update(plaintext_padded) +        try: +            unpadded += unpadder.finalize() +        except ValueError: +            raise InvalidToken +        return unpadded + + +class MultiFernet(object): +    def __init__(self, fernets): +        fernets = list(fernets) +        if not fernets: +            raise ValueError( +                "MultiFernet requires at least one Fernet instance" +            ) +        self._fernets = fernets + +    def encrypt(self, msg): +        return self._fernets[0].encrypt(msg) + +    def decrypt(self, msg, ttl=None): +        for f in self._fernets: +            try: +                return f.decrypt(msg, ttl) +            except InvalidToken: +                pass +        raise InvalidToken diff --git a/src/cryptography/hazmat/__init__.py b/src/cryptography/hazmat/__init__.py new file mode 100644 index 00000000..2f420574 --- /dev/null +++ b/src/cryptography/hazmat/__init__.py @@ -0,0 +1,14 @@ +# 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 diff --git a/src/cryptography/hazmat/backends/__init__.py b/src/cryptography/hazmat/backends/__init__.py new file mode 100644 index 00000000..b0f663fe --- /dev/null +++ b/src/cryptography/hazmat/backends/__init__.py @@ -0,0 +1,46 @@ +# 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 pkg_resources + +from cryptography.hazmat.backends.multibackend import MultiBackend + + +_available_backends_list = None + + +def _available_backends(): +    global _available_backends_list + +    if _available_backends_list is None: +        _available_backends_list = [ +            backend.load(require=False) +            for backend in pkg_resources.iter_entry_points( +                "cryptography.backends" +            ) +        ] + +    return _available_backends_list + +_default_backend = None + + +def default_backend(): +    global _default_backend + +    if _default_backend is None: +        _default_backend = MultiBackend(_available_backends()) + +    return _default_backend diff --git a/src/cryptography/hazmat/backends/commoncrypto/__init__.py b/src/cryptography/hazmat/backends/commoncrypto/__init__.py new file mode 100644 index 00000000..f080394f --- /dev/null +++ b/src/cryptography/hazmat/backends/commoncrypto/__init__.py @@ -0,0 +1,19 @@ +# 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.hazmat.backends.commoncrypto.backend import backend + + +__all__ = ["backend"] diff --git a/src/cryptography/hazmat/backends/commoncrypto/backend.py b/src/cryptography/hazmat/backends/commoncrypto/backend.py new file mode 100644 index 00000000..7bab979f --- /dev/null +++ b/src/cryptography/hazmat/backends/commoncrypto/backend.py @@ -0,0 +1,253 @@ +# 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 collections import namedtuple + +from cryptography import utils +from cryptography.exceptions import InternalError +from cryptography.hazmat.backends.commoncrypto.ciphers import ( +    _CipherContext, _GCMCipherContext +) +from cryptography.hazmat.backends.commoncrypto.hashes import _HashContext +from cryptography.hazmat.backends.commoncrypto.hmac import _HMACContext +from cryptography.hazmat.backends.interfaces import ( +    CipherBackend, HMACBackend, HashBackend, PBKDF2HMACBackend +) +from cryptography.hazmat.bindings.commoncrypto.binding import Binding +from cryptography.hazmat.primitives.ciphers.algorithms import ( +    AES, ARC4, Blowfish, CAST5, TripleDES +) +from cryptography.hazmat.primitives.ciphers.modes import ( +    CBC, CFB, CFB8, CTR, ECB, GCM, OFB +) + + +HashMethods = namedtuple( +    "HashMethods", ["ctx", "hash_init", "hash_update", "hash_final"] +) + + +@utils.register_interface(CipherBackend) +@utils.register_interface(HashBackend) +@utils.register_interface(HMACBackend) +@utils.register_interface(PBKDF2HMACBackend) +class Backend(object): +    """ +    CommonCrypto API wrapper. +    """ +    name = "commoncrypto" + +    def __init__(self): +        self._binding = Binding() +        self._ffi = self._binding.ffi +        self._lib = self._binding.lib + +        self._cipher_registry = {} +        self._register_default_ciphers() +        self._hash_mapping = { +            "md5": HashMethods( +                "CC_MD5_CTX *", self._lib.CC_MD5_Init, +                self._lib.CC_MD5_Update, self._lib.CC_MD5_Final +            ), +            "sha1": HashMethods( +                "CC_SHA1_CTX *", self._lib.CC_SHA1_Init, +                self._lib.CC_SHA1_Update, self._lib.CC_SHA1_Final +            ), +            "sha224": HashMethods( +                "CC_SHA256_CTX *", self._lib.CC_SHA224_Init, +                self._lib.CC_SHA224_Update, self._lib.CC_SHA224_Final +            ), +            "sha256": HashMethods( +                "CC_SHA256_CTX *", self._lib.CC_SHA256_Init, +                self._lib.CC_SHA256_Update, self._lib.CC_SHA256_Final +            ), +            "sha384": HashMethods( +                "CC_SHA512_CTX *", self._lib.CC_SHA384_Init, +                self._lib.CC_SHA384_Update, self._lib.CC_SHA384_Final +            ), +            "sha512": HashMethods( +                "CC_SHA512_CTX *", self._lib.CC_SHA512_Init, +                self._lib.CC_SHA512_Update, self._lib.CC_SHA512_Final +            ), +        } + +        self._supported_hmac_algorithms = { +            "md5": self._lib.kCCHmacAlgMD5, +            "sha1": self._lib.kCCHmacAlgSHA1, +            "sha224": self._lib.kCCHmacAlgSHA224, +            "sha256": self._lib.kCCHmacAlgSHA256, +            "sha384": self._lib.kCCHmacAlgSHA384, +            "sha512": self._lib.kCCHmacAlgSHA512, +        } + +        self._supported_pbkdf2_hmac_algorithms = { +            "sha1": self._lib.kCCPRFHmacAlgSHA1, +            "sha224": self._lib.kCCPRFHmacAlgSHA224, +            "sha256": self._lib.kCCPRFHmacAlgSHA256, +            "sha384": self._lib.kCCPRFHmacAlgSHA384, +            "sha512": self._lib.kCCPRFHmacAlgSHA512, +        } + +    def hash_supported(self, algorithm): +        return algorithm.name in self._hash_mapping + +    def hmac_supported(self, algorithm): +        return algorithm.name in self._supported_hmac_algorithms + +    def create_hash_ctx(self, algorithm): +        return _HashContext(self, algorithm) + +    def create_hmac_ctx(self, key, algorithm): +        return _HMACContext(self, key, algorithm) + +    def cipher_supported(self, cipher, mode): +        return (type(cipher), type(mode)) in self._cipher_registry + +    def create_symmetric_encryption_ctx(self, cipher, mode): +        if isinstance(mode, GCM): +            return _GCMCipherContext( +                self, cipher, mode, self._lib.kCCEncrypt +            ) +        else: +            return _CipherContext(self, cipher, mode, self._lib.kCCEncrypt) + +    def create_symmetric_decryption_ctx(self, cipher, mode): +        if isinstance(mode, GCM): +            return _GCMCipherContext( +                self, cipher, mode, self._lib.kCCDecrypt +            ) +        else: +            return _CipherContext(self, cipher, mode, self._lib.kCCDecrypt) + +    def pbkdf2_hmac_supported(self, algorithm): +        return algorithm.name in self._supported_pbkdf2_hmac_algorithms + +    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations, +                           key_material): +        alg_enum = self._supported_pbkdf2_hmac_algorithms[algorithm.name] +        buf = self._ffi.new("char[]", length) +        res = self._lib.CCKeyDerivationPBKDF( +            self._lib.kCCPBKDF2, +            key_material, +            len(key_material), +            salt, +            len(salt), +            alg_enum, +            iterations, +            buf, +            length +        ) +        self._check_cipher_response(res) + +        return self._ffi.buffer(buf)[:] + +    def _register_cipher_adapter(self, cipher_cls, cipher_const, mode_cls, +                                 mode_const): +        if (cipher_cls, mode_cls) in self._cipher_registry: +            raise ValueError("Duplicate registration for: {0} {1}.".format( +                cipher_cls, mode_cls) +            ) +        self._cipher_registry[cipher_cls, mode_cls] = (cipher_const, +                                                       mode_const) + +    def _register_default_ciphers(self): +        for mode_cls, mode_const in [ +            (CBC, self._lib.kCCModeCBC), +            (ECB, self._lib.kCCModeECB), +            (CFB, self._lib.kCCModeCFB), +            (CFB8, self._lib.kCCModeCFB8), +            (OFB, self._lib.kCCModeOFB), +            (CTR, self._lib.kCCModeCTR), +            (GCM, self._lib.kCCModeGCM), +        ]: +            self._register_cipher_adapter( +                AES, +                self._lib.kCCAlgorithmAES128, +                mode_cls, +                mode_const +            ) +        for mode_cls, mode_const in [ +            (CBC, self._lib.kCCModeCBC), +            (ECB, self._lib.kCCModeECB), +            (CFB, self._lib.kCCModeCFB), +            (CFB8, self._lib.kCCModeCFB8), +            (OFB, self._lib.kCCModeOFB), +        ]: +            self._register_cipher_adapter( +                TripleDES, +                self._lib.kCCAlgorithm3DES, +                mode_cls, +                mode_const +            ) +        for mode_cls, mode_const in [ +            (CBC, self._lib.kCCModeCBC), +            (ECB, self._lib.kCCModeECB), +            (CFB, self._lib.kCCModeCFB), +            (OFB, self._lib.kCCModeOFB) +        ]: +            self._register_cipher_adapter( +                Blowfish, +                self._lib.kCCAlgorithmBlowfish, +                mode_cls, +                mode_const +            ) +        for mode_cls, mode_const in [ +            (CBC, self._lib.kCCModeCBC), +            (ECB, self._lib.kCCModeECB), +            (CFB, self._lib.kCCModeCFB), +            (OFB, self._lib.kCCModeOFB), +            (CTR, self._lib.kCCModeCTR) +        ]: +            self._register_cipher_adapter( +                CAST5, +                self._lib.kCCAlgorithmCAST, +                mode_cls, +                mode_const +            ) +        self._register_cipher_adapter( +            ARC4, +            self._lib.kCCAlgorithmRC4, +            type(None), +            self._lib.kCCModeRC4 +        ) + +    def _check_cipher_response(self, response): +        if response == self._lib.kCCSuccess: +            return +        elif response == self._lib.kCCAlignmentError: +            # This error is not currently triggered due to a bug filed as +            # rdar://15589470 +            raise ValueError( +                "The length of the provided data is not a multiple of " +                "the block length." +            ) +        else: +            raise InternalError( +                "The backend returned an unknown error, consider filing a bug." +                " Code: {0}.".format(response) +            ) + +    def _release_cipher_ctx(self, ctx): +        """ +        Called by the garbage collector and used to safely dereference and +        release the context. +        """ +        if ctx[0] != self._ffi.NULL: +            res = self._lib.CCCryptorRelease(ctx[0]) +            self._check_cipher_response(res) +            ctx[0] = self._ffi.NULL + + +backend = Backend() diff --git a/src/cryptography/hazmat/backends/commoncrypto/ciphers.py b/src/cryptography/hazmat/backends/commoncrypto/ciphers.py new file mode 100644 index 00000000..d94746c6 --- /dev/null +++ b/src/cryptography/hazmat/backends/commoncrypto/ciphers.py @@ -0,0 +1,201 @@ +# 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.exceptions import ( +    InvalidTag, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.primitives import constant_time, interfaces +from cryptography.hazmat.primitives.ciphers.modes import ( +    CFB, CFB8, CTR, OFB +) + + +@utils.register_interface(interfaces.CipherContext) +class _CipherContext(object): +    def __init__(self, backend, cipher, mode, operation): +        self._backend = backend +        self._cipher = cipher +        self._mode = mode +        self._operation = operation +        # There is a bug in CommonCrypto where block ciphers do not raise +        # kCCAlignmentError when finalizing if you supply non-block aligned +        # data. To work around this we need to keep track of the block +        # alignment ourselves, but only for alg+mode combos that require +        # block alignment. OFB, CFB, and CTR make a block cipher algorithm +        # into a stream cipher so we don't need to track them (and thus their +        # block size is effectively 1 byte just like OpenSSL/CommonCrypto +        # treat RC4 and other stream cipher block sizes). +        # This bug has been filed as rdar://15589470 +        self._bytes_processed = 0 +        if (isinstance(cipher, interfaces.BlockCipherAlgorithm) and not +                isinstance(mode, (OFB, CFB, CFB8, CTR))): +            self._byte_block_size = cipher.block_size // 8 +        else: +            self._byte_block_size = 1 + +        registry = self._backend._cipher_registry +        try: +            cipher_enum, mode_enum = registry[type(cipher), type(mode)] +        except KeyError: +            raise UnsupportedAlgorithm( +                "cipher {0} in {1} mode is not supported " +                "by this backend.".format( +                    cipher.name, mode.name if mode else mode), +                _Reasons.UNSUPPORTED_CIPHER +            ) + +        ctx = self._backend._ffi.new("CCCryptorRef *") +        ctx = self._backend._ffi.gc(ctx, self._backend._release_cipher_ctx) + +        if isinstance(mode, interfaces.ModeWithInitializationVector): +            iv_nonce = mode.initialization_vector +        elif isinstance(mode, interfaces.ModeWithNonce): +            iv_nonce = mode.nonce +        else: +            iv_nonce = self._backend._ffi.NULL + +        if isinstance(mode, CTR): +            mode_option = self._backend._lib.kCCModeOptionCTR_BE +        else: +            mode_option = 0 + +        res = self._backend._lib.CCCryptorCreateWithMode( +            operation, +            mode_enum, cipher_enum, +            self._backend._lib.ccNoPadding, iv_nonce, +            cipher.key, len(cipher.key), +            self._backend._ffi.NULL, 0, 0, mode_option, ctx) +        self._backend._check_cipher_response(res) + +        self._ctx = ctx + +    def update(self, data): +        # Count bytes processed to handle block alignment. +        self._bytes_processed += len(data) +        buf = self._backend._ffi.new( +            "unsigned char[]", len(data) + self._byte_block_size - 1) +        outlen = self._backend._ffi.new("size_t *") +        res = self._backend._lib.CCCryptorUpdate( +            self._ctx[0], data, len(data), buf, +            len(data) + self._byte_block_size - 1, outlen) +        self._backend._check_cipher_response(res) +        return self._backend._ffi.buffer(buf)[:outlen[0]] + +    def finalize(self): +        # Raise error if block alignment is wrong. +        if self._bytes_processed % self._byte_block_size: +            raise ValueError( +                "The length of the provided data is not a multiple of " +                "the block length." +            ) +        buf = self._backend._ffi.new("unsigned char[]", self._byte_block_size) +        outlen = self._backend._ffi.new("size_t *") +        res = self._backend._lib.CCCryptorFinal( +            self._ctx[0], buf, len(buf), outlen) +        self._backend._check_cipher_response(res) +        self._backend._release_cipher_ctx(self._ctx) +        return self._backend._ffi.buffer(buf)[:outlen[0]] + + +@utils.register_interface(interfaces.AEADCipherContext) +@utils.register_interface(interfaces.AEADEncryptionContext) +class _GCMCipherContext(object): +    def __init__(self, backend, cipher, mode, operation): +        self._backend = backend +        self._cipher = cipher +        self._mode = mode +        self._operation = operation +        self._tag = None + +        registry = self._backend._cipher_registry +        try: +            cipher_enum, mode_enum = registry[type(cipher), type(mode)] +        except KeyError: +            raise UnsupportedAlgorithm( +                "cipher {0} in {1} mode is not supported " +                "by this backend.".format( +                    cipher.name, mode.name if mode else mode), +                _Reasons.UNSUPPORTED_CIPHER +            ) + +        ctx = self._backend._ffi.new("CCCryptorRef *") +        ctx = self._backend._ffi.gc(ctx, self._backend._release_cipher_ctx) + +        self._ctx = ctx + +        res = self._backend._lib.CCCryptorCreateWithMode( +            operation, +            mode_enum, cipher_enum, +            self._backend._lib.ccNoPadding, +            self._backend._ffi.NULL, +            cipher.key, len(cipher.key), +            self._backend._ffi.NULL, 0, 0, 0, self._ctx) +        self._backend._check_cipher_response(res) + +        res = self._backend._lib.CCCryptorGCMAddIV( +            self._ctx[0], +            mode.initialization_vector, +            len(mode.initialization_vector) +        ) +        self._backend._check_cipher_response(res) +        # CommonCrypto has a bug where calling update without at least one +        # call to authenticate_additional_data will result in null byte output +        # for ciphertext. The following empty byte string call prevents the +        # issue, which is present in at least 10.8 and 10.9. +        # Filed as rdar://18314544 +        self.authenticate_additional_data(b"") + +    def update(self, data): +        buf = self._backend._ffi.new("unsigned char[]", len(data)) +        args = (self._ctx[0], data, len(data), buf) +        if self._operation == self._backend._lib.kCCEncrypt: +            res = self._backend._lib.CCCryptorGCMEncrypt(*args) +        else: +            res = self._backend._lib.CCCryptorGCMDecrypt(*args) + +        self._backend._check_cipher_response(res) +        return self._backend._ffi.buffer(buf)[:] + +    def finalize(self): +        # CommonCrypto has a yet another bug where you must make at least one +        # call to update. If you pass just AAD and call finalize without a call +        # to update you'll get null bytes for tag. The following update call +        # prevents this issue, which is present in at least 10.8 and 10.9. +        # Filed as rdar://18314580 +        self.update(b"") +        tag_size = self._cipher.block_size // 8 +        tag_buf = self._backend._ffi.new("unsigned char[]", tag_size) +        tag_len = self._backend._ffi.new("size_t *", tag_size) +        res = self._backend._lib.CCCryptorGCMFinal( +            self._ctx[0], tag_buf, tag_len +        ) +        self._backend._check_cipher_response(res) +        self._backend._release_cipher_ctx(self._ctx) +        self._tag = self._backend._ffi.buffer(tag_buf)[:] +        if (self._operation == self._backend._lib.kCCDecrypt and +                not constant_time.bytes_eq( +                    self._tag[:len(self._mode.tag)], self._mode.tag +                )): +            raise InvalidTag +        return b"" + +    def authenticate_additional_data(self, data): +        res = self._backend._lib.CCCryptorGCMAddAAD( +            self._ctx[0], data, len(data) +        ) +        self._backend._check_cipher_response(res) + +    tag = utils.read_only_property("_tag") diff --git a/src/cryptography/hazmat/backends/commoncrypto/hashes.py b/src/cryptography/hazmat/backends/commoncrypto/hashes.py new file mode 100644 index 00000000..217f4e8c --- /dev/null +++ b/src/cryptography/hazmat/backends/commoncrypto/hashes.py @@ -0,0 +1,64 @@ +# 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.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import interfaces + + +@utils.register_interface(interfaces.HashContext) +class _HashContext(object): +    def __init__(self, backend, algorithm, ctx=None): +        self._algorithm = algorithm +        self._backend = backend + +        if ctx is None: +            try: +                methods = self._backend._hash_mapping[self.algorithm.name] +            except KeyError: +                raise UnsupportedAlgorithm( +                    "{0} is not a supported hash on this backend.".format( +                        algorithm.name), +                    _Reasons.UNSUPPORTED_HASH +                ) +            ctx = self._backend._ffi.new(methods.ctx) +            res = methods.hash_init(ctx) +            assert res == 1 + +        self._ctx = ctx + +    algorithm = utils.read_only_property("_algorithm") + +    def copy(self): +        methods = self._backend._hash_mapping[self.algorithm.name] +        new_ctx = self._backend._ffi.new(methods.ctx) +        # CommonCrypto has no APIs for copying hashes, so we have to copy the +        # underlying struct. +        new_ctx[0] = self._ctx[0] + +        return _HashContext(self._backend, self.algorithm, ctx=new_ctx) + +    def update(self, data): +        methods = self._backend._hash_mapping[self.algorithm.name] +        res = methods.hash_update(self._ctx, data, len(data)) +        assert res == 1 + +    def finalize(self): +        methods = self._backend._hash_mapping[self.algorithm.name] +        buf = self._backend._ffi.new("unsigned char[]", +                                     self.algorithm.digest_size) +        res = methods.hash_final(buf, self._ctx) +        assert res == 1 +        return self._backend._ffi.buffer(buf)[:] diff --git a/src/cryptography/hazmat/backends/commoncrypto/hmac.py b/src/cryptography/hazmat/backends/commoncrypto/hmac.py new file mode 100644 index 00000000..ee7e3abb --- /dev/null +++ b/src/cryptography/hazmat/backends/commoncrypto/hmac.py @@ -0,0 +1,68 @@ +# 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.exceptions import ( +    InvalidSignature, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.primitives import constant_time, interfaces + + +@utils.register_interface(interfaces.MACContext) +@utils.register_interface(interfaces.HashContext) +class _HMACContext(object): +    def __init__(self, backend, key, algorithm, ctx=None): +        self._algorithm = algorithm +        self._backend = backend +        if ctx is None: +            ctx = self._backend._ffi.new("CCHmacContext *") +            try: +                alg = self._backend._supported_hmac_algorithms[algorithm.name] +            except KeyError: +                raise UnsupportedAlgorithm( +                    "{0} is not a supported HMAC hash on this backend.".format( +                        algorithm.name), +                    _Reasons.UNSUPPORTED_HASH +                ) + +            self._backend._lib.CCHmacInit(ctx, alg, key, len(key)) + +        self._ctx = ctx +        self._key = key + +    algorithm = utils.read_only_property("_algorithm") + +    def copy(self): +        copied_ctx = self._backend._ffi.new("CCHmacContext *") +        # CommonCrypto has no APIs for copying HMACs, so we have to copy the +        # underlying struct. +        copied_ctx[0] = self._ctx[0] +        return _HMACContext( +            self._backend, self._key, self.algorithm, ctx=copied_ctx +        ) + +    def update(self, data): +        self._backend._lib.CCHmacUpdate(self._ctx, data, len(data)) + +    def finalize(self): +        buf = self._backend._ffi.new("unsigned char[]", +                                     self.algorithm.digest_size) +        self._backend._lib.CCHmacFinal(self._ctx, buf) +        return self._backend._ffi.buffer(buf)[:] + +    def verify(self, signature): +        digest = self.finalize() +        if not constant_time.bytes_eq(digest, signature): +            raise InvalidSignature("Signature did not match digest.") diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py new file mode 100644 index 00000000..ecb5bf48 --- /dev/null +++ b/src/cryptography/hazmat/backends/interfaces.py @@ -0,0 +1,261 @@ +# 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 abc + +import six + + +@six.add_metaclass(abc.ABCMeta) +class CipherBackend(object): +    @abc.abstractmethod +    def cipher_supported(self, cipher, mode): +        """ +        Return True if the given cipher and mode are supported. +        """ + +    @abc.abstractmethod +    def create_symmetric_encryption_ctx(self, cipher, mode): +        """ +        Get a CipherContext that can be used for encryption. +        """ + +    @abc.abstractmethod +    def create_symmetric_decryption_ctx(self, cipher, mode): +        """ +        Get a CipherContext that can be used for decryption. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class HashBackend(object): +    @abc.abstractmethod +    def hash_supported(self, algorithm): +        """ +        Return True if the hash algorithm is supported by this backend. +        """ + +    @abc.abstractmethod +    def create_hash_ctx(self, algorithm): +        """ +        Create a HashContext for calculating a message digest. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class HMACBackend(object): +    @abc.abstractmethod +    def hmac_supported(self, algorithm): +        """ +        Return True if the hash algorithm is supported for HMAC by this +        backend. +        """ + +    @abc.abstractmethod +    def create_hmac_ctx(self, key, algorithm): +        """ +        Create a MACContext for calculating a message authentication code. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class CMACBackend(object): +    @abc.abstractmethod +    def cmac_algorithm_supported(self, algorithm): +        """ +        Returns True if the block cipher is supported for CMAC by this backend +        """ + +    @abc.abstractmethod +    def create_cmac_ctx(self, algorithm): +        """ +        Create a MACContext for calculating a message authentication code. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class PBKDF2HMACBackend(object): +    @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. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class RSABackend(object): +    @abc.abstractmethod +    def generate_rsa_private_key(self, public_exponent, key_size): +        """ +        Generate an RSAPrivateKey instance with public_exponent and a modulus +        of key_size bits. +        """ + +    @abc.abstractmethod +    def rsa_padding_supported(self, padding): +        """ +        Returns True if the backend supports the given padding options. +        """ + +    @abc.abstractmethod +    def generate_rsa_parameters_supported(self, public_exponent, key_size): +        """ +        Returns True if the backend supports the given parameters for key +        generation. +        """ + +    @abc.abstractmethod +    def load_rsa_private_numbers(self, numbers): +        """ +        Returns an RSAPrivateKey provider. +        """ + +    @abc.abstractmethod +    def load_rsa_public_numbers(self, numbers): +        """ +        Returns an RSAPublicKey provider. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class DSABackend(object): +    @abc.abstractmethod +    def generate_dsa_parameters(self, key_size): +        """ +        Generate a DSAParameters instance with a modulus of key_size bits. +        """ + +    @abc.abstractmethod +    def generate_dsa_private_key(self, parameters): +        """ +        Generate a DSAPrivateKey instance with parameters as a DSAParameters +        object. +        """ + +    @abc.abstractmethod +    def generate_dsa_private_key_and_parameters(self, key_size): +        """ +        Generate a DSAPrivateKey instance using key size only. +        """ + +    @abc.abstractmethod +    def dsa_hash_supported(self, algorithm): +        """ +        Return True if the hash algorithm is supported by the backend for DSA. +        """ + +    @abc.abstractmethod +    def dsa_parameters_supported(self, p, q, g): +        """ +        Return True if the parameters are supported by the backend for DSA. +        """ + +    @abc.abstractmethod +    def load_dsa_private_numbers(self, numbers): +        """ +        Returns a DSAPrivateKey provider. +        """ + +    @abc.abstractmethod +    def load_dsa_public_numbers(self, numbers): +        """ +        Returns a DSAPublicKey provider. +        """ + +    @abc.abstractmethod +    def load_dsa_parameter_numbers(self, numbers): +        """ +        Returns a DSAParameters provider. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class EllipticCurveBackend(object): +    @abc.abstractmethod +    def elliptic_curve_signature_algorithm_supported( +        self, signature_algorithm, curve +    ): +        """ +        Returns True if the backend supports the named elliptic curve with the +        specified signature algorithm. +        """ + +    @abc.abstractmethod +    def elliptic_curve_supported(self, curve): +        """ +        Returns True if the backend supports the named elliptic curve. +        """ + +    @abc.abstractmethod +    def generate_elliptic_curve_private_key(self, curve): +        """ +        Return an object conforming to the EllipticCurvePrivateKey interface. +        """ + +    @abc.abstractmethod +    def load_elliptic_curve_public_numbers(self, numbers): +        """ +        Return an EllipticCurvePublicKey provider using the given numbers. +        """ + +    @abc.abstractmethod +    def load_elliptic_curve_private_numbers(self, numbers): +        """ +        Return an EllipticCurvePublicKey provider using the given numbers. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class PEMSerializationBackend(object): +    @abc.abstractmethod +    def load_pem_private_key(self, data, password): +        """ +        Loads a private key from PEM encoded data, using the provided password +        if the data is encrypted. +        """ + +    @abc.abstractmethod +    def load_pem_public_key(self, data): +        """ +        Loads a public key from PEM encoded data. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class TraditionalOpenSSLSerializationBackend(object): +    @abc.abstractmethod +    def load_traditional_openssl_pem_private_key(self, data, password): +        """ +        Load a private key from PEM encoded data, using password if the data +        is encrypted. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class PKCS8SerializationBackend(object): +    @abc.abstractmethod +    def load_pkcs8_pem_private_key(self, data, password): +        """ +        Load a private key from PKCS8 encoded data, using password if the data +        is encrypted. +        """ diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py new file mode 100644 index 00000000..c62b790c --- /dev/null +++ b/src/cryptography/hazmat/backends/multibackend.py @@ -0,0 +1,358 @@ +# 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 warnings + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.backends.interfaces import ( +    CMACBackend, CipherBackend, DSABackend, EllipticCurveBackend, HMACBackend, +    HashBackend, PBKDF2HMACBackend, PEMSerializationBackend, +    PKCS8SerializationBackend, RSABackend, +    TraditionalOpenSSLSerializationBackend +) + + +@utils.register_interface(CMACBackend) +@utils.register_interface(CipherBackend) +@utils.register_interface(HashBackend) +@utils.register_interface(HMACBackend) +@utils.register_interface(PBKDF2HMACBackend) +@utils.register_interface(PKCS8SerializationBackend) +@utils.register_interface(RSABackend) +@utils.register_interface(TraditionalOpenSSLSerializationBackend) +@utils.register_interface(DSABackend) +@utils.register_interface(EllipticCurveBackend) +@utils.register_interface(PEMSerializationBackend) +class MultiBackend(object): +    name = "multibackend" + +    def __init__(self, backends): +        self._backends = backends + +    def _filtered_backends(self, interface): +        for b in self._backends: +            if isinstance(b, interface): +                yield b + +    def cipher_supported(self, cipher, mode): +        return any( +            b.cipher_supported(cipher, mode) +            for b in self._filtered_backends(CipherBackend) +        ) + +    def create_symmetric_encryption_ctx(self, cipher, mode): +        for b in self._filtered_backends(CipherBackend): +            try: +                return b.create_symmetric_encryption_ctx(cipher, mode) +            except UnsupportedAlgorithm: +                pass +        raise UnsupportedAlgorithm( +            "cipher {0} in {1} mode is not supported by this backend.".format( +                cipher.name, mode.name if mode else mode), +            _Reasons.UNSUPPORTED_CIPHER +        ) + +    def create_symmetric_decryption_ctx(self, cipher, mode): +        for b in self._filtered_backends(CipherBackend): +            try: +                return b.create_symmetric_decryption_ctx(cipher, mode) +            except UnsupportedAlgorithm: +                pass +        raise UnsupportedAlgorithm( +            "cipher {0} in {1} mode is not supported by this backend.".format( +                cipher.name, mode.name if mode else mode), +            _Reasons.UNSUPPORTED_CIPHER +        ) + +    def hash_supported(self, algorithm): +        return any( +            b.hash_supported(algorithm) +            for b in self._filtered_backends(HashBackend) +        ) + +    def create_hash_ctx(self, algorithm): +        for b in self._filtered_backends(HashBackend): +            try: +                return b.create_hash_ctx(algorithm) +            except UnsupportedAlgorithm: +                pass +        raise UnsupportedAlgorithm( +            "{0} is not a supported hash on this backend.".format( +                algorithm.name), +            _Reasons.UNSUPPORTED_HASH +        ) + +    def hmac_supported(self, algorithm): +        return any( +            b.hmac_supported(algorithm) +            for b in self._filtered_backends(HMACBackend) +        ) + +    def create_hmac_ctx(self, key, algorithm): +        for b in self._filtered_backends(HMACBackend): +            try: +                return b.create_hmac_ctx(key, algorithm) +            except UnsupportedAlgorithm: +                pass +        raise UnsupportedAlgorithm( +            "{0} is not a supported hash on this backend.".format( +                algorithm.name), +            _Reasons.UNSUPPORTED_HASH +        ) + +    def pbkdf2_hmac_supported(self, algorithm): +        return any( +            b.pbkdf2_hmac_supported(algorithm) +            for b in self._filtered_backends(PBKDF2HMACBackend) +        ) + +    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations, +                           key_material): +        for b in self._filtered_backends(PBKDF2HMACBackend): +            try: +                return b.derive_pbkdf2_hmac( +                    algorithm, length, salt, iterations, key_material +                ) +            except UnsupportedAlgorithm: +                pass +        raise UnsupportedAlgorithm( +            "{0} is not a supported hash on this backend.".format( +                algorithm.name), +            _Reasons.UNSUPPORTED_HASH +        ) + +    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("RSA is not supported by the backend.", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def generate_rsa_parameters_supported(self, public_exponent, key_size): +        for b in self._filtered_backends(RSABackend): +            return b.generate_rsa_parameters_supported( +                public_exponent, key_size +            ) +        raise UnsupportedAlgorithm("RSA is not supported by the backend.", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def rsa_padding_supported(self, padding): +        for b in self._filtered_backends(RSABackend): +            return b.rsa_padding_supported(padding) +        raise UnsupportedAlgorithm("RSA is not supported by the backend.", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def load_rsa_private_numbers(self, numbers): +        for b in self._filtered_backends(RSABackend): +            return b.load_rsa_private_numbers(numbers) + +        raise UnsupportedAlgorithm("RSA is not supported by the backend", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def load_rsa_public_numbers(self, numbers): +        for b in self._filtered_backends(RSABackend): +            return b.load_rsa_public_numbers(numbers) + +        raise UnsupportedAlgorithm("RSA is not supported by the backend", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def generate_dsa_parameters(self, key_size): +        for b in self._filtered_backends(DSABackend): +            return b.generate_dsa_parameters(key_size) +        raise UnsupportedAlgorithm("DSA is not supported by the backend.", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def generate_dsa_private_key(self, parameters): +        for b in self._filtered_backends(DSABackend): +            return b.generate_dsa_private_key(parameters) +        raise UnsupportedAlgorithm("DSA is not supported by the backend.", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def generate_dsa_private_key_and_parameters(self, key_size): +        for b in self._filtered_backends(DSABackend): +            return b.generate_dsa_private_key_and_parameters(key_size) +        raise UnsupportedAlgorithm("DSA is not supported by the backend.", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def dsa_hash_supported(self, algorithm): +        for b in self._filtered_backends(DSABackend): +            return b.dsa_hash_supported(algorithm) +        raise UnsupportedAlgorithm("DSA is not supported by the backend.", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def dsa_parameters_supported(self, p, q, g): +        for b in self._filtered_backends(DSABackend): +            return b.dsa_parameters_supported(p, q, g) +        raise UnsupportedAlgorithm("DSA is not supported by the backend.", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def load_dsa_public_numbers(self, numbers): +        for b in self._filtered_backends(DSABackend): +            return b.load_dsa_public_numbers(numbers) +        raise UnsupportedAlgorithm("DSA is not supported by the backend.", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def load_dsa_private_numbers(self, numbers): +        for b in self._filtered_backends(DSABackend): +            return b.load_dsa_private_numbers(numbers) +        raise UnsupportedAlgorithm("DSA is not supported by the backend.", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def load_dsa_parameter_numbers(self, numbers): +        for b in self._filtered_backends(DSABackend): +            return b.load_dsa_parameter_numbers(numbers) +        raise UnsupportedAlgorithm("DSA is not supported by the backend.", +                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def cmac_algorithm_supported(self, algorithm): +        return any( +            b.cmac_algorithm_supported(algorithm) +            for b in self._filtered_backends(CMACBackend) +        ) + +    def create_cmac_ctx(self, algorithm): +        for b in self._filtered_backends(CMACBackend): +            try: +                return b.create_cmac_ctx(algorithm) +            except UnsupportedAlgorithm: +                pass +        raise UnsupportedAlgorithm("This backend does not support CMAC.", +                                   _Reasons.UNSUPPORTED_CIPHER) + +    def elliptic_curve_supported(self, curve): +        return any( +            b.elliptic_curve_supported(curve) +            for b in self._filtered_backends(EllipticCurveBackend) +        ) + +    def elliptic_curve_signature_algorithm_supported( +        self, signature_algorithm, curve +    ): +        return any( +            b.elliptic_curve_signature_algorithm_supported( +                signature_algorithm, curve +            ) +            for b in self._filtered_backends(EllipticCurveBackend) +        ) + +    def generate_elliptic_curve_private_key(self, curve): +        for b in self._filtered_backends(EllipticCurveBackend): +            try: +                return b.generate_elliptic_curve_private_key(curve) +            except UnsupportedAlgorithm: +                continue + +        raise UnsupportedAlgorithm( +            "This backend does not support this elliptic curve.", +            _Reasons.UNSUPPORTED_ELLIPTIC_CURVE +        ) + +    def elliptic_curve_private_key_from_numbers(self, numbers): +        warnings.warn( +            "elliptic_curve_private_key_from_numbers is deprecated and will " +            "be removed in a future version.", +            utils.DeprecatedIn06, +            stacklevel=2 +        ) +        for b in self._filtered_backends(EllipticCurveBackend): +            try: +                return b.elliptic_curve_private_key_from_numbers(numbers) +            except UnsupportedAlgorithm: +                continue + +        raise UnsupportedAlgorithm( +            "This backend does not support this elliptic curve.", +            _Reasons.UNSUPPORTED_ELLIPTIC_CURVE +        ) + +    def load_elliptic_curve_private_numbers(self, numbers): +        for b in self._filtered_backends(EllipticCurveBackend): +            try: +                return b.load_elliptic_curve_private_numbers(numbers) +            except UnsupportedAlgorithm: +                continue + +        raise UnsupportedAlgorithm( +            "This backend does not support this elliptic curve.", +            _Reasons.UNSUPPORTED_ELLIPTIC_CURVE +        ) + +    def elliptic_curve_public_key_from_numbers(self, numbers): +        warnings.warn( +            "elliptic_curve_public_key_from_numbers is deprecated and will " +            "be removed in a future version.", +            utils.DeprecatedIn06, +            stacklevel=2 +        ) +        for b in self._filtered_backends(EllipticCurveBackend): +            try: +                return b.elliptic_curve_public_key_from_numbers(numbers) +            except UnsupportedAlgorithm: +                continue + +        raise UnsupportedAlgorithm( +            "This backend does not support this elliptic curve.", +            _Reasons.UNSUPPORTED_ELLIPTIC_CURVE +        ) + +    def load_elliptic_curve_public_numbers(self, numbers): +        for b in self._filtered_backends(EllipticCurveBackend): +            try: +                return b.load_elliptic_curve_public_numbers(numbers) +            except UnsupportedAlgorithm: +                continue + +        raise UnsupportedAlgorithm( +            "This backend does not support this elliptic curve.", +            _Reasons.UNSUPPORTED_ELLIPTIC_CURVE +        ) + +    def load_pem_private_key(self, data, password): +        for b in self._filtered_backends(PEMSerializationBackend): +            return b.load_pem_private_key(data, password) + +        raise UnsupportedAlgorithm( +            "This backend does not support this key serialization.", +            _Reasons.UNSUPPORTED_SERIALIZATION +        ) + +    def load_pem_public_key(self, data): +        for b in self._filtered_backends(PEMSerializationBackend): +            return b.load_pem_public_key(data) + +        raise UnsupportedAlgorithm( +            "This backend does not support this key serialization.", +            _Reasons.UNSUPPORTED_SERIALIZATION +        ) + +    def load_pkcs8_pem_private_key(self, data, password): +        for b in self._filtered_backends(PKCS8SerializationBackend): +            return b.load_pkcs8_pem_private_key(data, password) + +        raise UnsupportedAlgorithm( +            "This backend does not support this key serialization.", +            _Reasons.UNSUPPORTED_SERIALIZATION +        ) + +    def load_traditional_openssl_pem_private_key(self, data, password): +        for b in self._filtered_backends( +            TraditionalOpenSSLSerializationBackend +        ): +            return b.load_traditional_openssl_pem_private_key(data, password) + +        raise UnsupportedAlgorithm( +            "This backend does not support this key serialization.", +            _Reasons.UNSUPPORTED_SERIALIZATION +        ) diff --git a/src/cryptography/hazmat/backends/openssl/__init__.py b/src/cryptography/hazmat/backends/openssl/__init__.py new file mode 100644 index 00000000..25885e18 --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/__init__.py @@ -0,0 +1,19 @@ +# 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.hazmat.backends.openssl.backend import backend + + +__all__ = ["backend"] diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py new file mode 100644 index 00000000..bb1a3f3d --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -0,0 +1,1036 @@ +# 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 collections +import itertools +import warnings +from contextlib import contextmanager + +import six + +from cryptography import utils +from cryptography.exceptions import ( +    InternalError, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.interfaces import ( +    CMACBackend, CipherBackend, DSABackend, EllipticCurveBackend, HMACBackend, +    HashBackend, PBKDF2HMACBackend, PEMSerializationBackend, +    PKCS8SerializationBackend, RSABackend, +    TraditionalOpenSSLSerializationBackend +) +from cryptography.hazmat.backends.openssl.ciphers import ( +    _AESCTRCipherContext, _CipherContext +) +from cryptography.hazmat.backends.openssl.cmac import _CMACContext +from cryptography.hazmat.backends.openssl.dsa import ( +    _DSAParameters, _DSAPrivateKey, _DSAPublicKey +) +from cryptography.hazmat.backends.openssl.ec import ( +    _EllipticCurvePrivateKey, _EllipticCurvePublicKey +) +from cryptography.hazmat.backends.openssl.hashes import _HashContext +from cryptography.hazmat.backends.openssl.hmac import _HMACContext +from cryptography.hazmat.backends.openssl.rsa import ( +    _RSAPrivateKey, _RSAPublicKey +) +from cryptography.hazmat.bindings.openssl.binding import Binding +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa +from cryptography.hazmat.primitives.asymmetric.padding import ( +    MGF1, OAEP, PKCS1v15, PSS +) +from cryptography.hazmat.primitives.ciphers.algorithms import ( +    AES, ARC4, Blowfish, CAST5, Camellia, IDEA, SEED, TripleDES +) +from cryptography.hazmat.primitives.ciphers.modes import ( +    CBC, CFB, CFB8, CTR, ECB, GCM, OFB +) + + +_MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"]) +_OpenSSLError = collections.namedtuple("_OpenSSLError", +                                       ["code", "lib", "func", "reason"]) + + +@utils.register_interface(CipherBackend) +@utils.register_interface(CMACBackend) +@utils.register_interface(DSABackend) +@utils.register_interface(EllipticCurveBackend) +@utils.register_interface(HashBackend) +@utils.register_interface(HMACBackend) +@utils.register_interface(PBKDF2HMACBackend) +@utils.register_interface(PKCS8SerializationBackend) +@utils.register_interface(RSABackend) +@utils.register_interface(TraditionalOpenSSLSerializationBackend) +@utils.register_interface(PEMSerializationBackend) +class Backend(object): +    """ +    OpenSSL API binding interfaces. +    """ +    name = "openssl" + +    def __init__(self): +        self._binding = Binding() +        self._ffi = self._binding.ffi +        self._lib = self._binding.lib + +        self._binding.init_static_locks() + +        # adds all ciphers/digests for EVP +        self._lib.OpenSSL_add_all_algorithms() +        # registers available SSL/TLS ciphers and digests +        self._lib.SSL_library_init() +        # loads error strings for libcrypto and libssl functions +        self._lib.SSL_load_error_strings() + +        self._cipher_registry = {} +        self._register_default_ciphers() +        self.activate_osrandom_engine() + +    def activate_builtin_random(self): +        # Obtain a new structural reference. +        e = self._lib.ENGINE_get_default_RAND() +        if e != self._ffi.NULL: +            self._lib.ENGINE_unregister_RAND(e) +            # Reset the RNG to use the new engine. +            self._lib.RAND_cleanup() +            # decrement the structural reference from get_default_RAND +            res = self._lib.ENGINE_finish(e) +            assert res == 1 + +    def activate_osrandom_engine(self): +        # Unregister and free the current engine. +        self.activate_builtin_random() +        # Fetches an engine by id and returns it. This creates a structural +        # reference. +        e = self._lib.ENGINE_by_id(self._lib.Cryptography_osrandom_engine_id) +        assert e != self._ffi.NULL +        # Initialize the engine for use. This adds a functional reference. +        res = self._lib.ENGINE_init(e) +        assert res == 1 +        # Set the engine as the default RAND provider. +        res = self._lib.ENGINE_set_default_RAND(e) +        assert res == 1 +        # Decrement the structural ref incremented by ENGINE_by_id. +        res = self._lib.ENGINE_free(e) +        assert res == 1 +        # Decrement the functional ref incremented by ENGINE_init. +        res = self._lib.ENGINE_finish(e) +        assert res == 1 +        # Reset the RNG to use the new engine. +        self._lib.RAND_cleanup() + +    def openssl_version_text(self): +        """ +        Friendly string name of the loaded OpenSSL library. This is not +        necessarily the same version as it was compiled against. + +        Example: OpenSSL 1.0.1e 11 Feb 2013 +        """ +        return self._ffi.string( +            self._lib.SSLeay_version(self._lib.SSLEAY_VERSION) +        ).decode("ascii") + +    def create_hmac_ctx(self, key, algorithm): +        return _HMACContext(self, key, algorithm) + +    def hash_supported(self, algorithm): +        digest = self._lib.EVP_get_digestbyname(algorithm.name.encode("ascii")) +        return digest != self._ffi.NULL + +    def hmac_supported(self, algorithm): +        return self.hash_supported(algorithm) + +    def create_hash_ctx(self, algorithm): +        return _HashContext(self, algorithm) + +    def cipher_supported(self, cipher, mode): +        if self._evp_cipher_supported(cipher, mode): +            return True +        elif isinstance(mode, CTR) and isinstance(cipher, AES): +            return True +        else: +            return False + +    def _evp_cipher_supported(self, cipher, mode): +        try: +            adapter = self._cipher_registry[type(cipher), type(mode)] +        except KeyError: +            return False +        evp_cipher = adapter(self, cipher, mode) +        return self._ffi.NULL != evp_cipher + +    def register_cipher_adapter(self, cipher_cls, mode_cls, adapter): +        if (cipher_cls, mode_cls) in self._cipher_registry: +            raise ValueError("Duplicate registration for: {0} {1}.".format( +                cipher_cls, mode_cls) +            ) +        self._cipher_registry[cipher_cls, mode_cls] = adapter + +    def _register_default_ciphers(self): +        for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8]: +            self.register_cipher_adapter( +                AES, +                mode_cls, +                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}") +            ) +        for mode_cls in [CBC, CTR, ECB, OFB, CFB]: +            self.register_cipher_adapter( +                Camellia, +                mode_cls, +                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}") +            ) +        for mode_cls in [CBC, CFB, CFB8, OFB]: +            self.register_cipher_adapter( +                TripleDES, +                mode_cls, +                GetCipherByName("des-ede3-{mode.name}") +            ) +        self.register_cipher_adapter( +            TripleDES, +            ECB, +            GetCipherByName("des-ede3") +        ) +        for mode_cls in [CBC, CFB, OFB, ECB]: +            self.register_cipher_adapter( +                Blowfish, +                mode_cls, +                GetCipherByName("bf-{mode.name}") +            ) +        for mode_cls in [CBC, CFB, OFB, ECB]: +            self.register_cipher_adapter( +                SEED, +                mode_cls, +                GetCipherByName("seed-{mode.name}") +            ) +        for cipher_cls, mode_cls in itertools.product( +            [CAST5, IDEA], +            [CBC, OFB, CFB, ECB], +        ): +            self.register_cipher_adapter( +                cipher_cls, +                mode_cls, +                GetCipherByName("{cipher.name}-{mode.name}") +            ) +        self.register_cipher_adapter( +            ARC4, +            type(None), +            GetCipherByName("rc4") +        ) +        self.register_cipher_adapter( +            AES, +            GCM, +            GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}") +        ) + +    def create_symmetric_encryption_ctx(self, cipher, mode): +        if (isinstance(mode, CTR) and isinstance(cipher, AES) +                and not self._evp_cipher_supported(cipher, mode)): +            # This is needed to provide support for AES CTR mode in OpenSSL +            # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5 +            # extended life ends 2020). +            return _AESCTRCipherContext(self, cipher, mode) +        else: +            return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) + +    def create_symmetric_decryption_ctx(self, cipher, mode): +        if (isinstance(mode, CTR) and isinstance(cipher, AES) +                and not self._evp_cipher_supported(cipher, mode)): +            # This is needed to provide support for AES CTR mode in OpenSSL +            # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5 +            # extended life ends 2020). +            return _AESCTRCipherContext(self, cipher, mode) +        else: +            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: +            if not isinstance(algorithm, hashes.SHA1): +                raise UnsupportedAlgorithm( +                    "This version of OpenSSL only supports PBKDF2HMAC with " +                    "SHA1.", +                    _Reasons.UNSUPPORTED_HASH +                ) +            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 _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 _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( +                error.code, self._err_string(error.code) +            ) +        ) + +    def _bn_to_int(self, bn): +        if six.PY3: +            # Python 3 has constant time from_bytes, so use that. + +            bn_num_bytes = (self._lib.BN_num_bits(bn) + 7) // 8 +            bin_ptr = self._ffi.new("unsigned char[]", bn_num_bytes) +            bin_len = self._lib.BN_bn2bin(bn, bin_ptr) +            assert bin_len > 0 +            assert bin_ptr != self._ffi.NULL +            return int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big") + +        else: +            # Under Python 2 the best we can do is hex() + +            hex_cdata = self._lib.BN_bn2hex(bn) +            assert hex_cdata != self._ffi.NULL +            hex_str = self._ffi.string(hex_cdata) +            self._lib.OPENSSL_free(hex_cdata) +            return int(hex_str, 16) + +    def _int_to_bn(self, num, bn=None): +        """ +        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. +        """ + +        if bn is None: +            bn = self._ffi.NULL + +        if six.PY3: +            # Python 3 has constant time to_bytes, so use that. + +            binary = num.to_bytes(int(num.bit_length() / 8.0 + 1), "big") +            bn_ptr = self._lib.BN_bin2bn(binary, len(binary), bn) +            assert bn_ptr != self._ffi.NULL +            return bn_ptr + +        else: +            # Under Python 2 the best we can do is hex() + +            hex_num = hex(num).rstrip("L").lstrip("0x").encode("ascii") or b"0" +            bn_ptr = self._ffi.new("BIGNUM **") +            bn_ptr[0] = bn +            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): +        rsa._verify_rsa_parameters(public_exponent, key_size) + +        rsa_cdata = self._lib.RSA_new() +        assert rsa_cdata != self._ffi.NULL +        rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + +        bn = self._int_to_bn(public_exponent) +        bn = self._ffi.gc(bn, self._lib.BN_free) + +        res = self._lib.RSA_generate_key_ex( +            rsa_cdata, key_size, bn, self._ffi.NULL +        ) +        assert res == 1 + +        return _RSAPrivateKey(self, rsa_cdata) + +    def generate_rsa_parameters_supported(self, public_exponent, key_size): +        return (public_exponent >= 3 and public_exponent & 1 != 0 and +                key_size >= 512) + +    def load_rsa_private_numbers(self, numbers): +        rsa._check_private_key_components( +            numbers.p, +            numbers.q, +            numbers.d, +            numbers.dmp1, +            numbers.dmq1, +            numbers.iqmp, +            numbers.public_numbers.e, +            numbers.public_numbers.n +        ) +        rsa_cdata = self._lib.RSA_new() +        assert rsa_cdata != self._ffi.NULL +        rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) +        rsa_cdata.p = self._int_to_bn(numbers.p) +        rsa_cdata.q = self._int_to_bn(numbers.q) +        rsa_cdata.d = self._int_to_bn(numbers.d) +        rsa_cdata.dmp1 = self._int_to_bn(numbers.dmp1) +        rsa_cdata.dmq1 = self._int_to_bn(numbers.dmq1) +        rsa_cdata.iqmp = self._int_to_bn(numbers.iqmp) +        rsa_cdata.e = self._int_to_bn(numbers.public_numbers.e) +        rsa_cdata.n = self._int_to_bn(numbers.public_numbers.n) +        res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL) +        assert res == 1 + +        return _RSAPrivateKey(self, rsa_cdata) + +    def load_rsa_public_numbers(self, numbers): +        rsa._check_public_key_components(numbers.e, numbers.n) +        rsa_cdata = self._lib.RSA_new() +        assert rsa_cdata != self._ffi.NULL +        rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) +        rsa_cdata.e = self._int_to_bn(numbers.e) +        rsa_cdata.n = self._int_to_bn(numbers.n) +        res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL) +        assert res == 1 + +        return _RSAPublicKey(self, rsa_cdata) + +    def _bytes_to_bio(self, data): +        """ +        Return a _MemoryBIO namedtuple of (BIO, char*). + +        The char* is the storage for the BIO and it must stay alive until the +        BIO is finished with. +        """ +        data_char_p = self._ffi.new("char[]", data) +        bio = self._lib.BIO_new_mem_buf( +            data_char_p, len(data) +        ) +        assert bio != self._ffi.NULL + +        return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_char_p) + +    def _evp_pkey_to_private_key(self, evp_pkey): +        """ +        Return the appropriate type of PrivateKey given an evp_pkey cdata +        pointer. +        """ + +        type = evp_pkey.type + +        if type == self._lib.EVP_PKEY_RSA: +            rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) +            assert rsa_cdata != self._ffi.NULL +            rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) +            return _RSAPrivateKey(self, rsa_cdata) +        elif type == self._lib.EVP_PKEY_DSA: +            dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) +            assert dsa_cdata != self._ffi.NULL +            dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) +            return _DSAPrivateKey(self, dsa_cdata) +        elif (self._lib.Cryptography_HAS_EC == 1 and +              type == self._lib.EVP_PKEY_EC): +            ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) +            assert ec_cdata != self._ffi.NULL +            ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) +            return _EllipticCurvePrivateKey(self, ec_cdata) +        else: +            raise UnsupportedAlgorithm("Unsupported key type.") + +    def _evp_pkey_to_public_key(self, evp_pkey): +        """ +        Return the appropriate type of PublicKey given an evp_pkey cdata +        pointer. +        """ + +        type = evp_pkey.type + +        if type == self._lib.EVP_PKEY_RSA: +            rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) +            assert rsa_cdata != self._ffi.NULL +            rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) +            return _RSAPublicKey(self, rsa_cdata) +        elif type == self._lib.EVP_PKEY_DSA: +            dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) +            assert dsa_cdata != self._ffi.NULL +            dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) +            return _DSAPublicKey(self, dsa_cdata) +        elif (self._lib.Cryptography_HAS_EC == 1 and +              type == self._lib.EVP_PKEY_EC): +            ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) +            assert ec_cdata != self._ffi.NULL +            ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) +            return _EllipticCurvePublicKey(self, ec_cdata) +        else: +            raise UnsupportedAlgorithm("Unsupported key type.") + +    def _pem_password_cb(self, password): +        """ +        Generate a pem_password_cb function pointer that copied the password to +        OpenSSL as required and returns the number of bytes copied. + +        typedef int pem_password_cb(char *buf, int size, +                                    int rwflag, void *userdata); + +        Useful for decrypting PKCS8 files and so on. + +        Returns a tuple of (cdata function pointer, callback function). +        """ + +        def pem_password_cb(buf, size, writing, userdata): +            pem_password_cb.called += 1 + +            if not password: +                pem_password_cb.exception = TypeError( +                    "Password was not given but private key is encrypted." +                ) +                return 0 +            elif len(password) < size: +                pw_buf = self._ffi.buffer(buf, size) +                pw_buf[:len(password)] = password +                return len(password) +            else: +                pem_password_cb.exception = ValueError( +                    "Passwords longer than {0} bytes are not supported " +                    "by this backend.".format(size - 1) +                ) +                return 0 + +        pem_password_cb.called = 0 +        pem_password_cb.exception = None + +        return ( +            self._ffi.callback("int (char *, int, int, void *)", +                               pem_password_cb), +            pem_password_cb +        ) + +    def _mgf1_hash_supported(self, algorithm): +        if self._lib.Cryptography_HAS_MGF1_MD: +            return self.hash_supported(algorithm) +        else: +            return isinstance(algorithm, hashes.SHA1) + +    def rsa_padding_supported(self, padding): +        if isinstance(padding, PKCS1v15): +            return True +        elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1): +            return self._mgf1_hash_supported(padding._mgf._algorithm) +        elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1): +            return isinstance(padding._mgf._algorithm, hashes.SHA1) +        else: +            return False + +    def generate_dsa_parameters(self, key_size): +        if key_size not in (1024, 2048, 3072): +            raise ValueError( +                "Key size must be 1024 or 2048 or 3072 bits.") + +        if (self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f and +                key_size > 1024): +            raise ValueError( +                "Key size must be 1024 because OpenSSL < 1.0.0 doesn't " +                "support larger key sizes.") + +        ctx = self._lib.DSA_new() +        assert ctx != self._ffi.NULL +        ctx = self._ffi.gc(ctx, self._lib.DSA_free) + +        res = self._lib.DSA_generate_parameters_ex( +            ctx, key_size, self._ffi.NULL, 0, +            self._ffi.NULL, self._ffi.NULL, self._ffi.NULL +        ) + +        assert res == 1 + +        return _DSAParameters(self, ctx) + +    def generate_dsa_private_key(self, parameters): +        ctx = self._lib.DSA_new() +        assert ctx != self._ffi.NULL +        ctx = self._ffi.gc(ctx, self._lib.DSA_free) +        ctx.p = self._lib.BN_dup(parameters._dsa_cdata.p) +        ctx.q = self._lib.BN_dup(parameters._dsa_cdata.q) +        ctx.g = self._lib.BN_dup(parameters._dsa_cdata.g) + +        self._lib.DSA_generate_key(ctx) + +        return _DSAPrivateKey(self, ctx) + +    def generate_dsa_private_key_and_parameters(self, key_size): +        parameters = self.generate_dsa_parameters(key_size) +        return self.generate_dsa_private_key(parameters) + +    def load_dsa_private_numbers(self, numbers): +        dsa._check_dsa_private_numbers(numbers) +        parameter_numbers = numbers.public_numbers.parameter_numbers + +        dsa_cdata = self._lib.DSA_new() +        assert dsa_cdata != self._ffi.NULL +        dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) + +        dsa_cdata.p = self._int_to_bn(parameter_numbers.p) +        dsa_cdata.q = self._int_to_bn(parameter_numbers.q) +        dsa_cdata.g = self._int_to_bn(parameter_numbers.g) +        dsa_cdata.pub_key = self._int_to_bn(numbers.public_numbers.y) +        dsa_cdata.priv_key = self._int_to_bn(numbers.x) + +        return _DSAPrivateKey(self, dsa_cdata) + +    def load_dsa_public_numbers(self, numbers): +        dsa._check_dsa_parameters(numbers.parameter_numbers) +        dsa_cdata = self._lib.DSA_new() +        assert dsa_cdata != self._ffi.NULL +        dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) + +        dsa_cdata.p = self._int_to_bn(numbers.parameter_numbers.p) +        dsa_cdata.q = self._int_to_bn(numbers.parameter_numbers.q) +        dsa_cdata.g = self._int_to_bn(numbers.parameter_numbers.g) +        dsa_cdata.pub_key = self._int_to_bn(numbers.y) + +        return _DSAPublicKey(self, dsa_cdata) + +    def load_dsa_parameter_numbers(self, numbers): +        dsa._check_dsa_parameters(numbers) +        dsa_cdata = self._lib.DSA_new() +        assert dsa_cdata != self._ffi.NULL +        dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) + +        dsa_cdata.p = self._int_to_bn(numbers.p) +        dsa_cdata.q = self._int_to_bn(numbers.q) +        dsa_cdata.g = self._int_to_bn(numbers.g) + +        return _DSAParameters(self, dsa_cdata) + +    def dsa_hash_supported(self, algorithm): +        if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f: +            return isinstance(algorithm, hashes.SHA1) +        else: +            return self.hash_supported(algorithm) + +    def dsa_parameters_supported(self, p, q, g): +        if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f: +            return (utils.bit_length(p) <= 1024 and utils.bit_length(q) <= 160) +        else: +            return True + +    def cmac_algorithm_supported(self, algorithm): +        return ( +            self._lib.Cryptography_HAS_CMAC == 1 +            and self.cipher_supported(algorithm, CBC( +                b"\x00" * algorithm.block_size)) +        ) + +    def create_cmac_ctx(self, algorithm): +        return _CMACContext(self, algorithm) + +    def load_pem_private_key(self, data, password): +        return self._load_key( +            self._lib.PEM_read_bio_PrivateKey, +            self._evp_pkey_to_private_key, +            data, +            password, +        ) + +    def load_pem_public_key(self, data): +        return self._load_key( +            self._lib.PEM_read_bio_PUBKEY, +            self._evp_pkey_to_public_key, +            data, +            None, +        ) + +    def load_traditional_openssl_pem_private_key(self, data, password): +        warnings.warn( +            "load_traditional_openssl_pem_private_key is deprecated and will " +            "be removed in a future version, use load_pem_private_key " +            "instead.", +            utils.DeprecatedIn06, +            stacklevel=2 +        ) +        return self.load_pem_private_key(data, password) + +    def load_pkcs8_pem_private_key(self, data, password): +        warnings.warn( +            "load_pkcs8_pem_private_key is deprecated and will be removed in a" +            " future version, use load_pem_private_key instead.", +            utils.DeprecatedIn06, +            stacklevel=2 +        ) +        return self.load_pem_private_key(data, password) + +    def _load_key(self, openssl_read_func, convert_func, data, password): +        mem_bio = self._bytes_to_bio(data) + +        password_callback, password_func = self._pem_password_cb(password) + +        evp_pkey = openssl_read_func( +            mem_bio.bio, +            self._ffi.NULL, +            password_callback, +            self._ffi.NULL +        ) + +        if evp_pkey == self._ffi.NULL: +            if password_func.exception is not None: +                errors = self._consume_errors() +                assert errors +                raise password_func.exception +            else: +                self._handle_key_loading_error() + +        evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + +        if password is not None and password_func.called == 0: +            raise TypeError( +                "Password was given but private key is not encrypted.") + +        assert ( +            (password is not None and password_func.called == 1) or +            password is None +        ) + +        return convert_func(evp_pkey) + +    def _handle_key_loading_error(self): +        errors = self._consume_errors() + +        if not errors: +            raise ValueError("Could not unserialize key data.") + +        elif errors[0][1:] in ( +            ( +                self._lib.ERR_LIB_EVP, +                self._lib.EVP_F_EVP_DECRYPTFINAL_EX, +                self._lib.EVP_R_BAD_DECRYPT +            ), +            ( +                self._lib.ERR_LIB_PKCS12, +                self._lib.PKCS12_F_PKCS12_PBE_CRYPT, +                self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR, +            ) +        ): +            raise ValueError("Bad decrypt. Incorrect password?") + +        elif errors[0][1:] in ( +            ( +                self._lib.ERR_LIB_PEM, +                self._lib.PEM_F_PEM_GET_EVP_CIPHER_INFO, +                self._lib.PEM_R_UNSUPPORTED_ENCRYPTION +            ), + +            ( +                self._lib.ERR_LIB_EVP, +                self._lib.EVP_F_EVP_PBE_CIPHERINIT, +                self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM +            ) +        ): +            raise UnsupportedAlgorithm( +                "PEM data is encrypted with an unsupported cipher", +                _Reasons.UNSUPPORTED_CIPHER +            ) + +        elif any( +            error[1:] == ( +                self._lib.ERR_LIB_EVP, +                self._lib.EVP_F_EVP_PKCS82PKEY, +                self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM +            ) +            for error in errors +        ): +            raise UnsupportedAlgorithm( +                "Unsupported public key algorithm.", +                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM +            ) + +        else: +            assert errors[0][1] in ( +                self._lib.ERR_LIB_EVP, +                self._lib.ERR_LIB_PEM, +                self._lib.ERR_LIB_ASN1, +            ) +            raise ValueError("Could not unserialize key data.") + +    def elliptic_curve_supported(self, curve): +        if self._lib.Cryptography_HAS_EC != 1: +            return False + +        try: +            curve_nid = self._elliptic_curve_to_nid(curve) +        except UnsupportedAlgorithm: +            curve_nid = self._lib.NID_undef + +        ctx = self._lib.EC_GROUP_new_by_curve_name(curve_nid) + +        if ctx == self._ffi.NULL: +            errors = self._consume_errors() +            assert ( +                curve_nid == self._lib.NID_undef or +                errors[0][1:] == ( +                    self._lib.ERR_LIB_EC, +                    self._lib.EC_F_EC_GROUP_NEW_BY_CURVE_NAME, +                    self._lib.EC_R_UNKNOWN_GROUP +                ) +            ) +            return False +        else: +            assert curve_nid != self._lib.NID_undef +            self._lib.EC_GROUP_free(ctx) +            return True + +    def elliptic_curve_signature_algorithm_supported( +        self, signature_algorithm, curve +    ): +        if self._lib.Cryptography_HAS_EC != 1: +            return False + +        # We only support ECDSA right now. +        if not isinstance(signature_algorithm, ec.ECDSA): +            return False + +        # Before 0.9.8m OpenSSL can't cope with digests longer than the curve. +        if ( +            self._lib.OPENSSL_VERSION_NUMBER < 0x009080df and +            curve.key_size < signature_algorithm.algorithm.digest_size * 8 +        ): +            return False + +        return self.elliptic_curve_supported(curve) + +    def generate_elliptic_curve_private_key(self, curve): +        """ +        Generate a new private key on the named curve. +        """ + +        if self.elliptic_curve_supported(curve): +            curve_nid = self._elliptic_curve_to_nid(curve) + +            ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) +            assert ec_cdata != self._ffi.NULL +            ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) + +            res = self._lib.EC_KEY_generate_key(ec_cdata) +            assert res == 1 + +            res = self._lib.EC_KEY_check_key(ec_cdata) +            assert res == 1 + +            return _EllipticCurvePrivateKey(self, ec_cdata) +        else: +            raise UnsupportedAlgorithm( +                "Backend object does not support {0}.".format(curve.name), +                _Reasons.UNSUPPORTED_ELLIPTIC_CURVE +            ) + +    def elliptic_curve_private_key_from_numbers(self, numbers): +        warnings.warn( +            "elliptic_curve_private_key_from_numbers is deprecated and will " +            "be removed in a future version.", +            utils.DeprecatedIn06, +            stacklevel=2 +        ) +        return self.load_elliptic_curve_private_numbers(numbers) + +    def load_elliptic_curve_private_numbers(self, numbers): +        public = numbers.public_numbers + +        curve_nid = self._elliptic_curve_to_nid(public.curve) + +        ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) +        assert ec_cdata != self._ffi.NULL +        ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) + +        ec_cdata = self._ec_key_set_public_key_affine_coordinates( +            ec_cdata, public.x, public.y) + +        res = self._lib.EC_KEY_set_private_key( +            ec_cdata, self._int_to_bn(numbers.private_value)) +        assert res == 1 + +        return _EllipticCurvePrivateKey(self, ec_cdata) + +    def elliptic_curve_public_key_from_numbers(self, numbers): +        warnings.warn( +            "elliptic_curve_public_key_from_numbers is deprecated and will be " +            "removed in a future version.", +            utils.DeprecatedIn06, +            stacklevel=2 +        ) +        return self.load_elliptic_curve_public_numbers(numbers) + +    def load_elliptic_curve_public_numbers(self, numbers): +        curve_nid = self._elliptic_curve_to_nid(numbers.curve) + +        ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) +        assert ec_cdata != self._ffi.NULL +        ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) + +        ec_cdata = self._ec_key_set_public_key_affine_coordinates( +            ec_cdata, numbers.x, numbers.y) + +        return _EllipticCurvePublicKey(self, ec_cdata) + +    def _elliptic_curve_to_nid(self, curve): +        """ +        Get the NID for a curve name. +        """ + +        curve_aliases = { +            "secp192r1": "prime192v1", +            "secp256r1": "prime256v1" +        } + +        curve_name = curve_aliases.get(curve.name, curve.name) + +        curve_nid = self._lib.OBJ_sn2nid(curve_name.encode()) +        if curve_nid == self._lib.NID_undef: +            raise UnsupportedAlgorithm( +                "{0} is not a supported elliptic curve".format(curve.name), +                _Reasons.UNSUPPORTED_ELLIPTIC_CURVE +            ) +        return curve_nid + +    @contextmanager +    def _tmp_bn_ctx(self): +        bn_ctx = self._lib.BN_CTX_new() +        assert bn_ctx != self._ffi.NULL +        bn_ctx = self._ffi.gc(bn_ctx, self._lib.BN_CTX_free) +        self._lib.BN_CTX_start(bn_ctx) +        try: +            yield bn_ctx +        finally: +            self._lib.BN_CTX_end(bn_ctx) + +    def _ec_key_determine_group_get_set_funcs(self, ctx): +        """ +        Given an EC_KEY determine the group and what methods are required to +        get/set point coordinates. +        """ +        assert ctx != self._ffi.NULL + +        nid_two_field = self._lib.OBJ_sn2nid(b"characteristic-two-field") +        assert nid_two_field != self._lib.NID_undef + +        group = self._lib.EC_KEY_get0_group(ctx) +        assert group != self._ffi.NULL + +        method = self._lib.EC_GROUP_method_of(group) +        assert method != self._ffi.NULL + +        nid = self._lib.EC_METHOD_get_field_type(method) +        assert nid != self._lib.NID_undef + +        if nid == nid_two_field and self._lib.Cryptography_HAS_EC2M: +            set_func = self._lib.EC_POINT_set_affine_coordinates_GF2m +            get_func = self._lib.EC_POINT_get_affine_coordinates_GF2m +        else: +            set_func = self._lib.EC_POINT_set_affine_coordinates_GFp +            get_func = self._lib.EC_POINT_get_affine_coordinates_GFp + +        assert set_func and get_func + +        return set_func, get_func, group + +    def _ec_key_set_public_key_affine_coordinates(self, ctx, x, y): +        """ +        This is a port of EC_KEY_set_public_key_affine_coordinates that was +        added in 1.0.1. + +        Sets the public key point in the EC_KEY context to the affine x and y +        values. +        """ + +        if x < 0 or y < 0: +            raise ValueError( +                "Invalid EC key. Both x and y must be non-negative." +            ) + +        bn_x = self._int_to_bn(x) +        bn_y = self._int_to_bn(y) + +        set_func, get_func, group = ( +            self._ec_key_determine_group_get_set_funcs(ctx) +        ) + +        point = self._lib.EC_POINT_new(group) +        assert point != self._ffi.NULL +        point = self._ffi.gc(point, self._lib.EC_POINT_free) + +        with self._tmp_bn_ctx() as bn_ctx: +            check_x = self._lib.BN_CTX_get(bn_ctx) +            check_y = self._lib.BN_CTX_get(bn_ctx) + +            res = set_func(group, point, bn_x, bn_y, bn_ctx) +            assert res == 1 + +            res = get_func(group, point, check_x, check_y, bn_ctx) +            assert res == 1 + +            assert self._lib.BN_cmp(bn_x, check_x) == 0 +            assert self._lib.BN_cmp(bn_y, check_y) == 0 + +        res = self._lib.EC_KEY_set_public_key(ctx, point) +        assert res == 1 + +        res = self._lib.EC_KEY_check_key(ctx) +        if res != 1: +            self._consume_errors() +            raise ValueError("Invalid EC key.") + +        return ctx + + +class GetCipherByName(object): +    def __init__(self, fmt): +        self._fmt = fmt + +    def __call__(self, backend, cipher, mode): +        cipher_name = self._fmt.format(cipher=cipher, mode=mode).lower() +        return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii")) + + +backend = Backend() diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py new file mode 100644 index 00000000..4ec2ac89 --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/ciphers.py @@ -0,0 +1,225 @@ +# 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.exceptions import InvalidTag, UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import interfaces +from cryptography.hazmat.primitives.ciphers.modes import GCM + + +@utils.register_interface(interfaces.CipherContext) +@utils.register_interface(interfaces.AEADCipherContext) +@utils.register_interface(interfaces.AEADEncryptionContext) +class _CipherContext(object): +    _ENCRYPT = 1 +    _DECRYPT = 0 + +    def __init__(self, backend, cipher, mode, operation): +        self._backend = backend +        self._cipher = cipher +        self._mode = mode +        self._operation = operation +        self._tag = None + +        if isinstance(self._cipher, interfaces.BlockCipherAlgorithm): +            self._block_size = self._cipher.block_size +        else: +            self._block_size = 1 + +        ctx = self._backend._lib.EVP_CIPHER_CTX_new() +        ctx = self._backend._ffi.gc( +            ctx, self._backend._lib.EVP_CIPHER_CTX_free +        ) + +        registry = self._backend._cipher_registry +        try: +            adapter = registry[type(cipher), type(mode)] +        except KeyError: +            raise UnsupportedAlgorithm( +                "cipher {0} in {1} mode is not supported " +                "by this backend.".format( +                    cipher.name, mode.name if mode else mode), +                _Reasons.UNSUPPORTED_CIPHER +            ) + +        evp_cipher = adapter(self._backend, cipher, mode) +        if evp_cipher == self._backend._ffi.NULL: +            raise UnsupportedAlgorithm( +                "cipher {0} in {1} mode is not supported " +                "by this backend.".format( +                    cipher.name, mode.name if mode else mode), +                _Reasons.UNSUPPORTED_CIPHER +            ) + +        if isinstance(mode, interfaces.ModeWithInitializationVector): +            iv_nonce = mode.initialization_vector +        elif isinstance(mode, interfaces.ModeWithNonce): +            iv_nonce = mode.nonce +        else: +            iv_nonce = self._backend._ffi.NULL +        # begin init with cipher and operation type +        res = self._backend._lib.EVP_CipherInit_ex(ctx, evp_cipher, +                                                   self._backend._ffi.NULL, +                                                   self._backend._ffi.NULL, +                                                   self._backend._ffi.NULL, +                                                   operation) +        assert res != 0 +        # set the key length to handle variable key ciphers +        res = self._backend._lib.EVP_CIPHER_CTX_set_key_length( +            ctx, len(cipher.key) +        ) +        assert res != 0 +        if isinstance(mode, GCM): +            res = self._backend._lib.EVP_CIPHER_CTX_ctrl( +                ctx, self._backend._lib.EVP_CTRL_GCM_SET_IVLEN, +                len(iv_nonce), self._backend._ffi.NULL +            ) +            assert res != 0 +            if operation == self._DECRYPT: +                res = self._backend._lib.EVP_CIPHER_CTX_ctrl( +                    ctx, self._backend._lib.EVP_CTRL_GCM_SET_TAG, +                    len(mode.tag), mode.tag +                ) +                assert res != 0 + +        # pass key/iv +        res = self._backend._lib.EVP_CipherInit_ex( +            ctx, +            self._backend._ffi.NULL, +            self._backend._ffi.NULL, +            cipher.key, +            iv_nonce, +            operation +        ) +        assert res != 0 +        # We purposely disable padding here as it's handled higher up in the +        # API. +        self._backend._lib.EVP_CIPHER_CTX_set_padding(ctx, 0) +        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 *") +        res = self._backend._lib.EVP_CipherUpdate(self._ctx, buf, outlen, data, +                                                  len(data)) +        assert res != 0 +        return self._backend._ffi.buffer(buf)[:outlen[0]] + +    def finalize(self): +        # OpenSSL 1.0.1 on Ubuntu 12.04 (and possibly other distributions) +        # appears to have a bug where you must make at least one call to update +        # even if you are only using authenticate_additional_data or the +        # GCM tag will be wrong. An (empty) call to update resolves this +        # and is harmless for all other versions of OpenSSL. +        if isinstance(self._mode, GCM): +            self.update(b"") + +        buf = self._backend._ffi.new("unsigned char[]", self._block_size) +        outlen = self._backend._ffi.new("int *") +        res = self._backend._lib.EVP_CipherFinal_ex(self._ctx, buf, outlen) +        if res == 0: +            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): +            block_byte_size = self._block_size // 8 +            tag_buf = self._backend._ffi.new( +                "unsigned char[]", block_byte_size +            ) +            res = self._backend._lib.EVP_CIPHER_CTX_ctrl( +                self._ctx, self._backend._lib.EVP_CTRL_GCM_GET_TAG, +                block_byte_size, tag_buf +            ) +            assert res != 0 +            self._tag = self._backend._ffi.buffer(tag_buf)[:] + +        res = self._backend._lib.EVP_CIPHER_CTX_cleanup(self._ctx) +        assert res == 1 +        return self._backend._ffi.buffer(buf)[:outlen[0]] + +    def authenticate_additional_data(self, data): +        outlen = self._backend._ffi.new("int *") +        res = self._backend._lib.EVP_CipherUpdate( +            self._ctx, self._backend._ffi.NULL, outlen, data, len(data) +        ) +        assert res != 0 + +    tag = utils.read_only_property("_tag") + + +@utils.register_interface(interfaces.CipherContext) +class _AESCTRCipherContext(object): +    """ +    This is needed to provide support for AES CTR mode in OpenSSL 0.9.8. It can +    be removed when we drop 0.9.8 support (RHEL5 extended life ends 2020). +    """ +    def __init__(self, backend, cipher, mode): +        self._backend = backend + +        self._key = self._backend._ffi.new("AES_KEY *") +        assert self._key != self._backend._ffi.NULL +        res = self._backend._lib.AES_set_encrypt_key( +            cipher.key, len(cipher.key) * 8, self._key +        ) +        assert res == 0 +        self._ecount = self._backend._ffi.new("char[]", 16) +        self._nonce = self._backend._ffi.new("char[16]", mode.nonce) +        self._num = self._backend._ffi.new("unsigned int *", 0) + +    def update(self, data): +        buf = self._backend._ffi.new("unsigned char[]", len(data)) +        self._backend._lib.AES_ctr128_encrypt( +            data, buf, len(data), self._key, self._nonce, +            self._ecount, self._num +        ) +        return self._backend._ffi.buffer(buf)[:] + +    def finalize(self): +        self._key = None +        self._ecount = None +        self._nonce = None +        self._num = None +        return b"" diff --git a/src/cryptography/hazmat/backends/openssl/cmac.py b/src/cryptography/hazmat/backends/openssl/cmac.py new file mode 100644 index 00000000..1ad6055b --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/cmac.py @@ -0,0 +1,89 @@ +# 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.exceptions import ( +    InvalidSignature, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.primitives import constant_time, interfaces +from cryptography.hazmat.primitives.ciphers.modes import CBC + + +@utils.register_interface(interfaces.MACContext) +class _CMACContext(object): +    def __init__(self, backend, algorithm, ctx=None): +        if not backend.cmac_algorithm_supported(algorithm): +            raise UnsupportedAlgorithm("This backend does not support CMAC.", +                                       _Reasons.UNSUPPORTED_CIPHER) + +        self._backend = backend +        self._key = algorithm.key +        self._algorithm = algorithm +        self._output_length = algorithm.block_size // 8 + +        if ctx is None: +            registry = self._backend._cipher_registry +            adapter = registry[type(algorithm), CBC] + +            evp_cipher = adapter(self._backend, algorithm, CBC) + +            ctx = self._backend._lib.CMAC_CTX_new() + +            assert ctx != self._backend._ffi.NULL +            ctx = self._backend._ffi.gc(ctx, self._backend._lib.CMAC_CTX_free) + +            self._backend._lib.CMAC_Init( +                ctx, self._key, len(self._key), +                evp_cipher, self._backend._ffi.NULL +            ) + +        self._ctx = ctx + +    algorithm = utils.read_only_property("_algorithm") + +    def update(self, data): +        res = self._backend._lib.CMAC_Update(self._ctx, data, len(data)) +        assert res == 1 + +    def finalize(self): +        buf = self._backend._ffi.new("unsigned char[]", self._output_length) +        length = self._backend._ffi.new("size_t *", self._output_length) +        res = self._backend._lib.CMAC_Final( +            self._ctx, buf, length +        ) +        assert res == 1 + +        self._ctx = None + +        return self._backend._ffi.buffer(buf)[:] + +    def copy(self): +        copied_ctx = self._backend._lib.CMAC_CTX_new() +        copied_ctx = self._backend._ffi.gc( +            copied_ctx, self._backend._lib.CMAC_CTX_free +        ) +        res = self._backend._lib.CMAC_CTX_copy( +            copied_ctx, self._ctx +        ) +        assert res == 1 +        return _CMACContext( +            self._backend, self._algorithm, ctx=copied_ctx +        ) + +    def verify(self, signature): +        digest = self.finalize() +        if not constant_time.bytes_eq(digest, signature): +            raise InvalidSignature("Signature did not match digest.") diff --git a/src/cryptography/hazmat/backends/openssl/dsa.py b/src/cryptography/hazmat/backends/openssl/dsa.py new file mode 100644 index 00000000..8652d50b --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/dsa.py @@ -0,0 +1,207 @@ +# 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.exceptions import InvalidSignature +from cryptography.hazmat.backends.openssl.utils import _truncate_digest +from cryptography.hazmat.primitives import hashes, interfaces +from cryptography.hazmat.primitives.asymmetric import dsa +from cryptography.hazmat.primitives.interfaces import ( +    DSAParametersWithNumbers, DSAPrivateKeyWithNumbers, DSAPublicKeyWithNumbers +) + + +def _truncate_digest_for_dsa(dsa_cdata, digest, backend): +    """ +    This function truncates digests that are longer than a given DS +    key's length so they can be signed. OpenSSL does this for us in +    1.0.0c+ and it isn't needed in 0.9.8, but that leaves us with three +    releases (1.0.0, 1.0.0a, and 1.0.0b) where this is a problem. This +    truncation is not required in 0.9.8 because DSA is limited to SHA-1. +    """ + +    order_bits = backend._lib.BN_num_bits(dsa_cdata.q) +    return _truncate_digest(digest, order_bits) + + +@utils.register_interface(interfaces.AsymmetricVerificationContext) +class _DSAVerificationContext(object): +    def __init__(self, backend, public_key, signature, algorithm): +        self._backend = backend +        self._public_key = public_key +        self._signature = signature +        self._algorithm = algorithm + +        self._hash_ctx = hashes.Hash(self._algorithm, self._backend) + +    def update(self, data): +        self._hash_ctx.update(data) + +    def verify(self): +        self._dsa_cdata = self._backend._ffi.gc(self._public_key._dsa_cdata, +                                                self._backend._lib.DSA_free) + +        data_to_verify = self._hash_ctx.finalize() + +        data_to_verify = _truncate_digest_for_dsa( +            self._dsa_cdata, data_to_verify, self._backend +        ) + +        # The first parameter passed to DSA_verify is unused by OpenSSL but +        # must be an integer. +        res = self._backend._lib.DSA_verify( +            0, data_to_verify, len(data_to_verify), self._signature, +            len(self._signature), self._public_key._dsa_cdata) + +        if res != 1: +            errors = self._backend._consume_errors() +            assert errors +            if res == -1: +                assert errors[0].lib == self._backend._lib.ERR_LIB_ASN1 + +            raise InvalidSignature + + +@utils.register_interface(interfaces.AsymmetricSignatureContext) +class _DSASignatureContext(object): +    def __init__(self, backend, private_key, algorithm): +        self._backend = backend +        self._private_key = private_key +        self._algorithm = algorithm +        self._hash_ctx = hashes.Hash(self._algorithm, self._backend) + +    def update(self, data): +        self._hash_ctx.update(data) + +    def finalize(self): +        data_to_sign = self._hash_ctx.finalize() +        data_to_sign = _truncate_digest_for_dsa( +            self._private_key._dsa_cdata, data_to_sign, self._backend +        ) +        sig_buf_len = self._backend._lib.DSA_size(self._private_key._dsa_cdata) +        sig_buf = self._backend._ffi.new("unsigned char[]", sig_buf_len) +        buflen = self._backend._ffi.new("unsigned int *") + +        # The first parameter passed to DSA_sign is unused by OpenSSL but +        # must be an integer. +        res = self._backend._lib.DSA_sign( +            0, data_to_sign, len(data_to_sign), sig_buf, +            buflen, self._private_key._dsa_cdata) +        assert res == 1 +        assert buflen[0] + +        return self._backend._ffi.buffer(sig_buf)[:buflen[0]] + + +@utils.register_interface(DSAParametersWithNumbers) +class _DSAParameters(object): +    def __init__(self, backend, dsa_cdata): +        self._backend = backend +        self._dsa_cdata = dsa_cdata + +    def parameter_numbers(self): +        return dsa.DSAParameterNumbers( +            p=self._backend._bn_to_int(self._dsa_cdata.p), +            q=self._backend._bn_to_int(self._dsa_cdata.q), +            g=self._backend._bn_to_int(self._dsa_cdata.g) +        ) + +    def generate_private_key(self): +        return self._backend.generate_dsa_private_key(self) + + +@utils.register_interface(DSAPrivateKeyWithNumbers) +class _DSAPrivateKey(object): +    def __init__(self, backend, dsa_cdata): +        self._backend = backend +        self._dsa_cdata = dsa_cdata +        self._key_size = self._backend._lib.BN_num_bits(self._dsa_cdata.p) + +    key_size = utils.read_only_property("_key_size") + +    def signer(self, signature_algorithm): +        return _DSASignatureContext(self._backend, self, signature_algorithm) + +    def private_numbers(self): +        return dsa.DSAPrivateNumbers( +            public_numbers=dsa.DSAPublicNumbers( +                parameter_numbers=dsa.DSAParameterNumbers( +                    p=self._backend._bn_to_int(self._dsa_cdata.p), +                    q=self._backend._bn_to_int(self._dsa_cdata.q), +                    g=self._backend._bn_to_int(self._dsa_cdata.g) +                ), +                y=self._backend._bn_to_int(self._dsa_cdata.pub_key) +            ), +            x=self._backend._bn_to_int(self._dsa_cdata.priv_key) +        ) + +    def public_key(self): +        dsa_cdata = self._backend._lib.DSA_new() +        assert dsa_cdata != self._backend._ffi.NULL +        dsa_cdata = self._backend._ffi.gc( +            dsa_cdata, self._backend._lib.DSA_free +        ) +        dsa_cdata.p = self._backend._lib.BN_dup(self._dsa_cdata.p) +        dsa_cdata.q = self._backend._lib.BN_dup(self._dsa_cdata.q) +        dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g) +        dsa_cdata.pub_key = self._backend._lib.BN_dup(self._dsa_cdata.pub_key) +        return _DSAPublicKey(self._backend, dsa_cdata) + +    def parameters(self): +        dsa_cdata = self._backend._lib.DSA_new() +        assert dsa_cdata != self._backend._ffi.NULL +        dsa_cdata = self._backend._ffi.gc( +            dsa_cdata, self._backend._lib.DSA_free +        ) +        dsa_cdata.p = self._backend._lib.BN_dup(self._dsa_cdata.p) +        dsa_cdata.q = self._backend._lib.BN_dup(self._dsa_cdata.q) +        dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g) +        return _DSAParameters(self._backend, dsa_cdata) + + +@utils.register_interface(DSAPublicKeyWithNumbers) +class _DSAPublicKey(object): +    def __init__(self, backend, dsa_cdata): +        self._backend = backend +        self._dsa_cdata = dsa_cdata +        self._key_size = self._backend._lib.BN_num_bits(self._dsa_cdata.p) + +    key_size = utils.read_only_property("_key_size") + +    def verifier(self, signature, signature_algorithm): +        return _DSAVerificationContext( +            self._backend, self, signature, signature_algorithm +        ) + +    def public_numbers(self): +        return dsa.DSAPublicNumbers( +            parameter_numbers=dsa.DSAParameterNumbers( +                p=self._backend._bn_to_int(self._dsa_cdata.p), +                q=self._backend._bn_to_int(self._dsa_cdata.q), +                g=self._backend._bn_to_int(self._dsa_cdata.g) +            ), +            y=self._backend._bn_to_int(self._dsa_cdata.pub_key) +        ) + +    def parameters(self): +        dsa_cdata = self._backend._lib.DSA_new() +        assert dsa_cdata != self._backend._ffi.NULL +        dsa_cdata = self._backend._ffi.gc( +            dsa_cdata, self._backend._lib.DSA_free +        ) +        dsa_cdata.p = self._backend._lib.BN_dup(self._dsa_cdata.p) +        dsa_cdata.q = self._backend._lib.BN_dup(self._dsa_cdata.q) +        dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g) +        return _DSAParameters(self._backend, dsa_cdata) diff --git a/src/cryptography/hazmat/backends/openssl/ec.py b/src/cryptography/hazmat/backends/openssl/ec.py new file mode 100644 index 00000000..13b0ddbb --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/ec.py @@ -0,0 +1,234 @@ +# 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.exceptions import ( +    InvalidSignature, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.openssl.utils import _truncate_digest +from cryptography.hazmat.primitives import hashes, interfaces +from cryptography.hazmat.primitives.asymmetric import ec + + +def _truncate_digest_for_ecdsa(ec_key_cdata, digest, backend): +    """ +    This function truncates digests that are longer than a given elliptic +    curve key's length so they can be signed. Since elliptic curve keys are +    much shorter than RSA keys many digests (e.g. SHA-512) may require +    truncation. +    """ + +    _lib = backend._lib +    _ffi = backend._ffi + +    group = _lib.EC_KEY_get0_group(ec_key_cdata) + +    with backend._tmp_bn_ctx() as bn_ctx: +        order = _lib.BN_CTX_get(bn_ctx) +        assert order != _ffi.NULL + +        res = _lib.EC_GROUP_get_order(group, order, bn_ctx) +        assert res == 1 + +        order_bits = _lib.BN_num_bits(order) + +    return _truncate_digest(digest, order_bits) + + +def _ec_key_curve_sn(backend, ec_key): +    group = backend._lib.EC_KEY_get0_group(ec_key) +    assert group != backend._ffi.NULL + +    nid = backend._lib.EC_GROUP_get_curve_name(group) +    assert nid != backend._lib.NID_undef + +    curve_name = backend._lib.OBJ_nid2sn(nid) +    assert curve_name != backend._ffi.NULL + +    sn = backend._ffi.string(curve_name).decode('ascii') +    return sn + + +def _sn_to_elliptic_curve(backend, sn): +    try: +        return ec._CURVE_TYPES[sn]() +    except KeyError: +        raise UnsupportedAlgorithm( +            "{0} is not a supported elliptic curve".format(sn), +            _Reasons.UNSUPPORTED_ELLIPTIC_CURVE +        ) + + +@utils.register_interface(interfaces.AsymmetricSignatureContext) +class _ECDSASignatureContext(object): +    def __init__(self, backend, private_key, algorithm): +        self._backend = backend +        self._private_key = private_key +        self._digest = hashes.Hash(algorithm, backend) + +    def update(self, data): +        self._digest.update(data) + +    def finalize(self): +        ec_key = self._private_key._ec_key + +        digest = self._digest.finalize() + +        digest = _truncate_digest_for_ecdsa(ec_key, digest, self._backend) + +        max_size = self._backend._lib.ECDSA_size(ec_key) +        assert max_size > 0 + +        sigbuf = self._backend._ffi.new("char[]", max_size) +        siglen_ptr = self._backend._ffi.new("unsigned int[]", 1) +        res = self._backend._lib.ECDSA_sign( +            0, +            digest, +            len(digest), +            sigbuf, +            siglen_ptr, +            ec_key +        ) +        assert res == 1 +        return self._backend._ffi.buffer(sigbuf)[:siglen_ptr[0]] + + +@utils.register_interface(interfaces.AsymmetricVerificationContext) +class _ECDSAVerificationContext(object): +    def __init__(self, backend, public_key, signature, algorithm): +        self._backend = backend +        self._public_key = public_key +        self._signature = signature +        self._digest = hashes.Hash(algorithm, backend) + +    def update(self, data): +        self._digest.update(data) + +    def verify(self): +        ec_key = self._public_key._ec_key + +        digest = self._digest.finalize() + +        digest = _truncate_digest_for_ecdsa(ec_key, digest, self._backend) + +        res = self._backend._lib.ECDSA_verify( +            0, +            digest, +            len(digest), +            self._signature, +            len(self._signature), +            ec_key +        ) +        if res != 1: +            self._backend._consume_errors() +            raise InvalidSignature +        return True + + +@utils.register_interface(interfaces.EllipticCurvePrivateKeyWithNumbers) +class _EllipticCurvePrivateKey(object): +    def __init__(self, backend, ec_key_cdata): +        self._backend = backend +        self._ec_key = ec_key_cdata + +        sn = _ec_key_curve_sn(backend, ec_key_cdata) +        self._curve = _sn_to_elliptic_curve(backend, sn) + +    curve = utils.read_only_property("_curve") + +    def signer(self, signature_algorithm): +        if isinstance(signature_algorithm, ec.ECDSA): +            return _ECDSASignatureContext( +                self._backend, self, signature_algorithm.algorithm +            ) +        else: +            raise UnsupportedAlgorithm( +                "Unsupported elliptic curve signature algorithm.", +                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def public_key(self): +        group = self._backend._lib.EC_KEY_get0_group(self._ec_key) +        assert group != self._backend._ffi.NULL + +        curve_nid = self._backend._lib.EC_GROUP_get_curve_name(group) + +        public_ec_key = self._backend._lib.EC_KEY_new_by_curve_name(curve_nid) +        assert public_ec_key != self._backend._ffi.NULL +        public_ec_key = self._backend._ffi.gc( +            public_ec_key, self._backend._lib.EC_KEY_free +        ) + +        point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key) +        assert point != self._backend._ffi.NULL + +        res = self._backend._lib.EC_KEY_set_public_key(public_ec_key, point) +        assert res == 1 + +        return _EllipticCurvePublicKey( +            self._backend, public_ec_key +        ) + +    def private_numbers(self): +        bn = self._backend._lib.EC_KEY_get0_private_key(self._ec_key) +        private_value = self._backend._bn_to_int(bn) +        return ec.EllipticCurvePrivateNumbers( +            private_value=private_value, +            public_numbers=self.public_key().public_numbers() +        ) + + +@utils.register_interface(interfaces.EllipticCurvePublicKeyWithNumbers) +class _EllipticCurvePublicKey(object): +    def __init__(self, backend, ec_key_cdata): +        self._backend = backend +        self._ec_key = ec_key_cdata + +        sn = _ec_key_curve_sn(backend, ec_key_cdata) +        self._curve = _sn_to_elliptic_curve(backend, sn) + +    curve = utils.read_only_property("_curve") + +    def verifier(self, signature, signature_algorithm): +        if isinstance(signature_algorithm, ec.ECDSA): +            return _ECDSAVerificationContext( +                self._backend, self, signature, signature_algorithm.algorithm +            ) +        else: +            raise UnsupportedAlgorithm( +                "Unsupported elliptic curve signature algorithm.", +                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + +    def public_numbers(self): +        set_func, get_func, group = ( +            self._backend._ec_key_determine_group_get_set_funcs(self._ec_key) +        ) +        point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key) +        assert point != self._backend._ffi.NULL + +        with self._backend._tmp_bn_ctx() as bn_ctx: +            bn_x = self._backend._lib.BN_CTX_get(bn_ctx) +            bn_y = self._backend._lib.BN_CTX_get(bn_ctx) + +            res = get_func(group, point, bn_x, bn_y, bn_ctx) +            assert res == 1 + +            x = self._backend._bn_to_int(bn_x) +            y = self._backend._bn_to_int(bn_y) + +        return ec.EllipticCurvePublicNumbers( +            x=x, +            y=y, +            curve=self._curve +        ) diff --git a/src/cryptography/hazmat/backends/openssl/hashes.py b/src/cryptography/hazmat/backends/openssl/hashes.py new file mode 100644 index 00000000..591c014a --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/hashes.py @@ -0,0 +1,71 @@ +# 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.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import interfaces + + +@utils.register_interface(interfaces.HashContext) +class _HashContext(object): +    def __init__(self, backend, algorithm, ctx=None): +        self._algorithm = algorithm + +        self._backend = backend + +        if ctx is None: +            ctx = self._backend._lib.EVP_MD_CTX_create() +            ctx = self._backend._ffi.gc(ctx, +                                        self._backend._lib.EVP_MD_CTX_destroy) +            evp_md = self._backend._lib.EVP_get_digestbyname( +                algorithm.name.encode("ascii")) +            if evp_md == self._backend._ffi.NULL: +                raise UnsupportedAlgorithm( +                    "{0} is not a supported hash on this backend.".format( +                        algorithm.name), +                    _Reasons.UNSUPPORTED_HASH +                ) +            res = self._backend._lib.EVP_DigestInit_ex(ctx, evp_md, +                                                       self._backend._ffi.NULL) +            assert res != 0 + +        self._ctx = ctx + +    algorithm = utils.read_only_property("_algorithm") + +    def copy(self): +        copied_ctx = self._backend._lib.EVP_MD_CTX_create() +        copied_ctx = self._backend._ffi.gc( +            copied_ctx, self._backend._lib.EVP_MD_CTX_destroy +        ) +        res = self._backend._lib.EVP_MD_CTX_copy_ex(copied_ctx, self._ctx) +        assert res != 0 +        return _HashContext(self._backend, self.algorithm, ctx=copied_ctx) + +    def update(self, data): +        res = self._backend._lib.EVP_DigestUpdate(self._ctx, data, len(data)) +        assert res != 0 + +    def finalize(self): +        buf = self._backend._ffi.new("unsigned char[]", +                                     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)[:outlen[0]] diff --git a/src/cryptography/hazmat/backends/openssl/hmac.py b/src/cryptography/hazmat/backends/openssl/hmac.py new file mode 100644 index 00000000..c324bd8c --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/hmac.py @@ -0,0 +1,90 @@ +# 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.exceptions import ( +    InvalidSignature, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.primitives import constant_time, interfaces + + +@utils.register_interface(interfaces.MACContext) +@utils.register_interface(interfaces.HashContext) +class _HMACContext(object): +    def __init__(self, backend, key, algorithm, ctx=None): +        self._algorithm = algorithm +        self._backend = backend + +        if ctx is None: +            ctx = self._backend._ffi.new("HMAC_CTX *") +            self._backend._lib.HMAC_CTX_init(ctx) +            ctx = self._backend._ffi.gc( +                ctx, self._backend._lib.HMAC_CTX_cleanup +            ) +            evp_md = self._backend._lib.EVP_get_digestbyname( +                algorithm.name.encode('ascii')) +            if evp_md == self._backend._ffi.NULL: +                raise UnsupportedAlgorithm( +                    "{0} is not a supported hash on this backend.".format( +                        algorithm.name), +                    _Reasons.UNSUPPORTED_HASH +                ) +            res = self._backend._lib.Cryptography_HMAC_Init_ex( +                ctx, key, len(key), evp_md, self._backend._ffi.NULL +            ) +            assert res != 0 + +        self._ctx = ctx +        self._key = key + +    algorithm = utils.read_only_property("_algorithm") + +    def copy(self): +        copied_ctx = self._backend._ffi.new("HMAC_CTX *") +        self._backend._lib.HMAC_CTX_init(copied_ctx) +        copied_ctx = self._backend._ffi.gc( +            copied_ctx, self._backend._lib.HMAC_CTX_cleanup +        ) +        res = self._backend._lib.Cryptography_HMAC_CTX_copy( +            copied_ctx, self._ctx +        ) +        assert res != 0 +        return _HMACContext( +            self._backend, self._key, self.algorithm, ctx=copied_ctx +        ) + +    def update(self, data): +        res = self._backend._lib.Cryptography_HMAC_Update( +            self._ctx, data, len(data) +        ) +        assert res != 0 + +    def finalize(self): +        buf = self._backend._ffi.new("unsigned char[]", +                                     self._backend._lib.EVP_MAX_MD_SIZE) +        outlen = self._backend._ffi.new("unsigned int *") +        res = self._backend._lib.Cryptography_HMAC_Final( +            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]] + +    def verify(self, signature): +        digest = self.finalize() +        if not constant_time.bytes_eq(digest, signature): +            raise InvalidSignature("Signature did not match digest.") diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py new file mode 100644 index 00000000..0a2a7f96 --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -0,0 +1,603 @@ +# 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 math + +from cryptography import utils +from cryptography.exceptions import ( +    AlreadyFinalized, InvalidSignature, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.primitives import hashes, interfaces +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric.padding import ( +    MGF1, OAEP, PKCS1v15, PSS +) +from cryptography.hazmat.primitives.interfaces import ( +    RSAPrivateKeyWithNumbers, RSAPublicKeyWithNumbers +) + + +def _get_rsa_pss_salt_length(pss, key_size, digest_size): +    salt = pss._salt_length + +    if salt is MGF1.MAX_LENGTH or salt is PSS.MAX_LENGTH: +        # bit length - 1 per RFC 3447 +        emlen = int(math.ceil((key_size - 1) / 8.0)) +        salt_length = emlen - digest_size - 2 +        assert salt_length >= 0 +        return salt_length +    else: +        return salt + + +def _enc_dec_rsa(backend, key, data, padding): +    if not isinstance(padding, interfaces.AsymmetricPadding): +        raise TypeError("Padding must be an instance of AsymmetricPadding.") + +    if isinstance(padding, PKCS1v15): +        padding_enum = backend._lib.RSA_PKCS1_PADDING +    elif isinstance(padding, OAEP): +        padding_enum = backend._lib.RSA_PKCS1_OAEP_PADDING +        if not isinstance(padding._mgf, MGF1): +            raise UnsupportedAlgorithm( +                "Only MGF1 is supported by this backend.", +                _Reasons.UNSUPPORTED_MGF +            ) + +        if not isinstance(padding._mgf._algorithm, hashes.SHA1): +            raise UnsupportedAlgorithm( +                "This backend supports only SHA1 inside MGF1 when " +                "using OAEP.", +                _Reasons.UNSUPPORTED_HASH +            ) + +        if padding._label is not None and padding._label != b"": +            raise ValueError("This backend does not support OAEP labels.") + +        if not isinstance(padding._algorithm, hashes.SHA1): +            raise UnsupportedAlgorithm( +                "This backend only supports SHA1 when using OAEP.", +                _Reasons.UNSUPPORTED_HASH +            ) +    else: +        raise UnsupportedAlgorithm( +            "{0} is not supported by this backend.".format( +                padding.name +            ), +            _Reasons.UNSUPPORTED_PADDING +        ) + +    if backend._lib.Cryptography_HAS_PKEY_CTX: +        return _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum) +    else: +        return _enc_dec_rsa_098(backend, key, data, padding_enum) + + +def _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum): +    if isinstance(key, _RSAPublicKey): +        init = backend._lib.EVP_PKEY_encrypt_init +        crypt = backend._lib.Cryptography_EVP_PKEY_encrypt +    else: +        init = backend._lib.EVP_PKEY_decrypt_init +        crypt = backend._lib.Cryptography_EVP_PKEY_decrypt + +    pkey_ctx = backend._lib.EVP_PKEY_CTX_new( +        key._evp_pkey, backend._ffi.NULL +    ) +    assert pkey_ctx != backend._ffi.NULL +    pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free) +    res = init(pkey_ctx) +    assert res == 1 +    res = backend._lib.EVP_PKEY_CTX_set_rsa_padding( +        pkey_ctx, padding_enum) +    assert res > 0 +    buf_size = backend._lib.EVP_PKEY_size(key._evp_pkey) +    assert buf_size > 0 +    outlen = backend._ffi.new("size_t *", buf_size) +    buf = backend._ffi.new("char[]", buf_size) +    res = crypt(pkey_ctx, buf, outlen, data, len(data)) +    if res <= 0: +        _handle_rsa_enc_dec_error(backend, key) + +    return backend._ffi.buffer(buf)[:outlen[0]] + + +def _enc_dec_rsa_098(backend, key, data, padding_enum): +    if isinstance(key, _RSAPublicKey): +        crypt = backend._lib.RSA_public_encrypt +    else: +        crypt = backend._lib.RSA_private_decrypt + +    key_size = backend._lib.RSA_size(key._rsa_cdata) +    assert key_size > 0 +    buf = backend._ffi.new("unsigned char[]", key_size) +    res = crypt(len(data), data, buf, key._rsa_cdata, padding_enum) +    if res < 0: +        _handle_rsa_enc_dec_error(backend, key) + +    return backend._ffi.buffer(buf)[:res] + + +def _handle_rsa_enc_dec_error(backend, key): +    errors = backend._consume_errors() +    assert errors +    assert errors[0].lib == backend._lib.ERR_LIB_RSA +    if isinstance(key, _RSAPublicKey): +        assert (errors[0].reason == +                backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE) +        raise ValueError( +            "Data too long for key size. Encrypt less data or use a " +            "larger key size." +        ) +    else: +        decoding_errors = [ +            backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_01, +            backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_02, +        ] +        if backend._lib.Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR: +            decoding_errors.append(backend._lib.RSA_R_PKCS_DECODING_ERROR) + +        assert errors[0].reason in decoding_errors +        raise ValueError("Decryption failed.") + + +@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.") + +        self._pkey_size = self._backend._lib.EVP_PKEY_size( +            self._private_key._evp_pkey +        ) + +        if isinstance(padding, PKCS1v15): +            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 +        elif isinstance(padding, PSS): +            if not isinstance(padding._mgf, MGF1): +                raise UnsupportedAlgorithm( +                    "Only MGF1 is supported by this backend.", +                    _Reasons.UNSUPPORTED_MGF +                ) + +            # Size of key in bytes - 2 is the maximum +            # PSS signature length (salt length is checked later) +            assert self._pkey_size > 0 +            if self._pkey_size - algorithm.digest_size - 2 < 0: +                raise ValueError("Digest too large for key size. Use a larger " +                                 "key.") + +            if not self._backend._mgf1_hash_supported(padding._mgf._algorithm): +                raise UnsupportedAlgorithm( +                    "When OpenSSL is older than 1.0.1 then only SHA1 is " +                    "supported with MGF1.", +                    _Reasons.UNSUPPORTED_HASH +                ) + +            if self._backend._lib.Cryptography_HAS_PKEY_CTX: +                self._finalize_method = self._finalize_pkey_ctx +                self._padding_enum = self._backend._lib.RSA_PKCS1_PSS_PADDING +            else: +                self._finalize_method = self._finalize_pss +        else: +            raise UnsupportedAlgorithm( +                "{0} is not supported by this backend.".format(padding.name), +                _Reasons.UNSUPPORTED_PADDING +            ) + +        self._padding = padding +        self._algorithm = algorithm +        self._hash_ctx = hashes.Hash(self._algorithm, self._backend) + +    def update(self, data): +        self._hash_ctx.update(data) + +    def finalize(self): +        evp_md = self._backend._lib.EVP_get_digestbyname( +            self._algorithm.name.encode("ascii")) +        assert evp_md != self._backend._ffi.NULL + +        return self._finalize_method(evp_md) + +    def _finalize_pkey_ctx(self, evp_md): +        pkey_ctx = self._backend._lib.EVP_PKEY_CTX_new( +            self._private_key._evp_pkey, self._backend._ffi.NULL +        ) +        assert pkey_ctx != self._backend._ffi.NULL +        pkey_ctx = self._backend._ffi.gc(pkey_ctx, +                                         self._backend._lib.EVP_PKEY_CTX_free) +        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 +        if isinstance(self._padding, PSS): +            res = self._backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen( +                pkey_ctx, +                _get_rsa_pss_salt_length( +                    self._padding, +                    self._private_key.key_size, +                    self._hash_ctx.algorithm.digest_size +                ) +            ) +            assert res > 0 + +            if self._backend._lib.Cryptography_HAS_MGF1_MD: +                # MGF1 MD is configurable in OpenSSL 1.0.1+ +                mgf1_md = self._backend._lib.EVP_get_digestbyname( +                    self._padding._mgf._algorithm.name.encode("ascii")) +                assert mgf1_md != self._backend._ffi.NULL +                res = self._backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md( +                    pkey_ctx, mgf1_md +                ) +                assert res > 0 +        data_to_sign = self._hash_ctx.finalize() +        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)) +        if res != 1: +            errors = self._backend._consume_errors() +            assert errors[0].lib == self._backend._lib.ERR_LIB_RSA +            reason = None +            if (errors[0].reason == +                    self._backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE): +                reason = ("Salt length too long for key size. Try using " +                          "MAX_LENGTH instead.") +            elif (errors[0].reason == +                    self._backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY): +                reason = "Digest too large for key size. Use a larger key." +            assert reason is not None +            raise ValueError(reason) + +        return self._backend._ffi.buffer(buf)[:] + +    def _finalize_pkcs1(self, evp_md): +        if self._hash_ctx._ctx is None: +            raise AlreadyFinalized("Context has already been finalized.") + +        sig_buf = self._backend._ffi.new("char[]", self._pkey_size) +        sig_len = self._backend._ffi.new("unsigned int *") +        res = self._backend._lib.EVP_SignFinal( +            self._hash_ctx._ctx._ctx, +            sig_buf, +            sig_len, +            self._private_key._evp_pkey +        ) +        self._hash_ctx.finalize() +        if res == 0: +            errors = self._backend._consume_errors() +            assert errors[0].lib == self._backend._lib.ERR_LIB_RSA +            assert (errors[0].reason == +                    self._backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY) +            raise ValueError("Digest too large for key size. Use a larger " +                             "key.") + +        return self._backend._ffi.buffer(sig_buf)[:sig_len[0]] + +    def _finalize_pss(self, evp_md): +        data_to_sign = self._hash_ctx.finalize() +        padded = self._backend._ffi.new("unsigned char[]", self._pkey_size) +        res = self._backend._lib.RSA_padding_add_PKCS1_PSS( +            self._private_key._rsa_cdata, +            padded, +            data_to_sign, +            evp_md, +            _get_rsa_pss_salt_length( +                self._padding, +                self._private_key.key_size, +                len(data_to_sign) +            ) +        ) +        if res != 1: +            errors = self._backend._consume_errors() +            assert errors[0].lib == self._backend._lib.ERR_LIB_RSA +            assert (errors[0].reason == +                    self._backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE) +            raise ValueError("Salt length too long for key size. Try using " +                             "MAX_LENGTH instead.") + +        sig_buf = self._backend._ffi.new("char[]", self._pkey_size) +        sig_len = self._backend._lib.RSA_private_encrypt( +            self._pkey_size, +            padded, +            sig_buf, +            self._private_key._rsa_cdata, +            self._backend._lib.RSA_NO_PADDING +        ) +        assert sig_len != -1 +        return self._backend._ffi.buffer(sig_buf)[:sig_len] + + +@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.") + +        self._pkey_size = self._backend._lib.EVP_PKEY_size( +            self._public_key._evp_pkey +        ) + +        if isinstance(padding, PKCS1v15): +            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 +        elif isinstance(padding, PSS): +            if not isinstance(padding._mgf, MGF1): +                raise UnsupportedAlgorithm( +                    "Only MGF1 is supported by this backend.", +                    _Reasons.UNSUPPORTED_MGF +                ) + +            # Size of key in bytes - 2 is the maximum +            # PSS signature length (salt length is checked later) +            assert self._pkey_size > 0 +            if self._pkey_size - algorithm.digest_size - 2 < 0: +                raise ValueError( +                    "Digest too large for key size. Check that you have the " +                    "correct key and digest algorithm." +                ) + +            if not self._backend._mgf1_hash_supported(padding._mgf._algorithm): +                raise UnsupportedAlgorithm( +                    "When OpenSSL is older than 1.0.1 then only SHA1 is " +                    "supported with MGF1.", +                    _Reasons.UNSUPPORTED_HASH +                ) + +            if self._backend._lib.Cryptography_HAS_PKEY_CTX: +                self._verify_method = self._verify_pkey_ctx +                self._padding_enum = self._backend._lib.RSA_PKCS1_PSS_PADDING +            else: +                self._verify_method = self._verify_pss +        else: +            raise UnsupportedAlgorithm( +                "{0} is not supported by this backend.".format(padding.name), +                _Reasons.UNSUPPORTED_PADDING +            ) + +        self._padding = padding +        self._algorithm = algorithm +        self._hash_ctx = hashes.Hash(self._algorithm, self._backend) + +    def update(self, data): +        self._hash_ctx.update(data) + +    def verify(self): +        evp_md = self._backend._lib.EVP_get_digestbyname( +            self._algorithm.name.encode("ascii")) +        assert evp_md != self._backend._ffi.NULL + +        self._verify_method(evp_md) + +    def _verify_pkey_ctx(self, evp_md): +        pkey_ctx = self._backend._lib.EVP_PKEY_CTX_new( +            self._public_key._evp_pkey, self._backend._ffi.NULL +        ) +        assert pkey_ctx != self._backend._ffi.NULL +        pkey_ctx = self._backend._ffi.gc(pkey_ctx, +                                         self._backend._lib.EVP_PKEY_CTX_free) +        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 +        if isinstance(self._padding, PSS): +            res = self._backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen( +                pkey_ctx, +                _get_rsa_pss_salt_length( +                    self._padding, +                    self._public_key.key_size, +                    self._hash_ctx.algorithm.digest_size +                ) +            ) +            assert res > 0 +            if self._backend._lib.Cryptography_HAS_MGF1_MD: +                # MGF1 MD is configurable in OpenSSL 1.0.1+ +                mgf1_md = self._backend._lib.EVP_get_digestbyname( +                    self._padding._mgf._algorithm.name.encode("ascii")) +                assert mgf1_md != self._backend._ffi.NULL +                res = self._backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md( +                    pkey_ctx, mgf1_md +                ) +                assert res > 0 + +        data_to_verify = self._hash_ctx.finalize() +        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: +            errors = self._backend._consume_errors() +            assert errors +            raise InvalidSignature + +    def _verify_pkcs1(self, evp_md): +        if self._hash_ctx._ctx is None: +            raise AlreadyFinalized("Context has already been finalized.") + +        res = self._backend._lib.EVP_VerifyFinal( +            self._hash_ctx._ctx._ctx, +            self._signature, +            len(self._signature), +            self._public_key._evp_pkey +        ) +        self._hash_ctx.finalize() +        # 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: +            errors = self._backend._consume_errors() +            assert errors +            raise InvalidSignature + +    def _verify_pss(self, evp_md): +        buf = self._backend._ffi.new("unsigned char[]", self._pkey_size) +        res = self._backend._lib.RSA_public_decrypt( +            len(self._signature), +            self._signature, +            buf, +            self._public_key._rsa_cdata, +            self._backend._lib.RSA_NO_PADDING +        ) +        if res != self._pkey_size: +            errors = self._backend._consume_errors() +            assert errors +            raise InvalidSignature + +        data_to_verify = self._hash_ctx.finalize() +        res = self._backend._lib.RSA_verify_PKCS1_PSS( +            self._public_key._rsa_cdata, +            data_to_verify, +            evp_md, +            buf, +            _get_rsa_pss_salt_length( +                self._padding, +                self._public_key.key_size, +                len(data_to_verify) +            ) +        ) +        if res != 1: +            errors = self._backend._consume_errors() +            assert errors +            raise InvalidSignature + + +@utils.register_interface(RSAPrivateKeyWithNumbers) +class _RSAPrivateKey(object): +    def __init__(self, backend, rsa_cdata): +        self._backend = backend +        self._rsa_cdata = rsa_cdata + +        evp_pkey = self._backend._lib.EVP_PKEY_new() +        assert evp_pkey != self._backend._ffi.NULL +        evp_pkey = self._backend._ffi.gc( +            evp_pkey, self._backend._lib.EVP_PKEY_free +        ) +        res = self._backend._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata) +        assert res == 1 +        self._evp_pkey = evp_pkey + +        self._key_size = self._backend._lib.BN_num_bits(self._rsa_cdata.n) + +    key_size = utils.read_only_property("_key_size") + +    def signer(self, padding, algorithm): +        return _RSASignatureContext(self._backend, self, padding, algorithm) + +    def decrypt(self, ciphertext, padding): +        key_size_bytes = int(math.ceil(self.key_size / 8.0)) +        if key_size_bytes != len(ciphertext): +            raise ValueError("Ciphertext length must be equal to key size.") + +        return _enc_dec_rsa(self._backend, self, ciphertext, padding) + +    def public_key(self): +        ctx = self._backend._lib.RSA_new() +        assert ctx != self._backend._ffi.NULL +        ctx = self._backend._ffi.gc(ctx, self._backend._lib.RSA_free) +        ctx.e = self._backend._lib.BN_dup(self._rsa_cdata.e) +        ctx.n = self._backend._lib.BN_dup(self._rsa_cdata.n) +        res = self._backend._lib.RSA_blinding_on(ctx, self._backend._ffi.NULL) +        assert res == 1 +        return _RSAPublicKey(self._backend, ctx) + +    def private_numbers(self): +        return rsa.RSAPrivateNumbers( +            p=self._backend._bn_to_int(self._rsa_cdata.p), +            q=self._backend._bn_to_int(self._rsa_cdata.q), +            d=self._backend._bn_to_int(self._rsa_cdata.d), +            dmp1=self._backend._bn_to_int(self._rsa_cdata.dmp1), +            dmq1=self._backend._bn_to_int(self._rsa_cdata.dmq1), +            iqmp=self._backend._bn_to_int(self._rsa_cdata.iqmp), +            public_numbers=rsa.RSAPublicNumbers( +                e=self._backend._bn_to_int(self._rsa_cdata.e), +                n=self._backend._bn_to_int(self._rsa_cdata.n), +            ) +        ) + + +@utils.register_interface(RSAPublicKeyWithNumbers) +class _RSAPublicKey(object): +    def __init__(self, backend, rsa_cdata): +        self._backend = backend +        self._rsa_cdata = rsa_cdata + +        evp_pkey = self._backend._lib.EVP_PKEY_new() +        assert evp_pkey != self._backend._ffi.NULL +        evp_pkey = self._backend._ffi.gc( +            evp_pkey, self._backend._lib.EVP_PKEY_free +        ) +        res = self._backend._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata) +        assert res == 1 +        self._evp_pkey = evp_pkey + +        self._key_size = self._backend._lib.BN_num_bits(self._rsa_cdata.n) + +    key_size = utils.read_only_property("_key_size") + +    def verifier(self, signature, padding, algorithm): +        return _RSAVerificationContext( +            self._backend, self, signature, padding, algorithm +        ) + +    def encrypt(self, plaintext, padding): +        return _enc_dec_rsa(self._backend, self, plaintext, padding) + +    def public_numbers(self): +        return rsa.RSAPublicNumbers( +            e=self._backend._bn_to_int(self._rsa_cdata.e), +            n=self._backend._bn_to_int(self._rsa_cdata.n), +        ) diff --git a/src/cryptography/hazmat/backends/openssl/utils.py b/src/cryptography/hazmat/backends/openssl/utils.py new file mode 100644 index 00000000..408b6146 --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/utils.py @@ -0,0 +1,35 @@ +# 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 + + +def _truncate_digest(digest, order_bits): +    digest_len = len(digest) + +    if 8 * digest_len > order_bits: +        digest_len = (order_bits + 7) // 8 +        digest = digest[:digest_len] + +    if 8 * digest_len > order_bits: +        rshift = 8 - (order_bits & 0x7) +        assert rshift > 0 and rshift < 8 + +        mask = 0xFF >> rshift << rshift + +        # Set the bottom rshift bits to 0 +        digest = digest[:-1] + six.int2byte(six.indexbytes(digest, -1) & mask) + +    return digest diff --git a/src/cryptography/hazmat/bindings/__init__.py b/src/cryptography/hazmat/bindings/__init__.py new file mode 100644 index 00000000..2f420574 --- /dev/null +++ b/src/cryptography/hazmat/bindings/__init__.py @@ -0,0 +1,14 @@ +# 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 diff --git a/src/cryptography/hazmat/bindings/commoncrypto/__init__.py b/src/cryptography/hazmat/bindings/commoncrypto/__init__.py new file mode 100644 index 00000000..2f420574 --- /dev/null +++ b/src/cryptography/hazmat/bindings/commoncrypto/__init__.py @@ -0,0 +1,14 @@ +# 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 diff --git a/src/cryptography/hazmat/bindings/commoncrypto/binding.py b/src/cryptography/hazmat/bindings/commoncrypto/binding.py new file mode 100644 index 00000000..bb950aac --- /dev/null +++ b/src/cryptography/hazmat/bindings/commoncrypto/binding.py @@ -0,0 +1,60 @@ +# 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.hazmat.bindings.utils import ( +    build_ffi_for_binding, load_library_for_binding, +) + + +class Binding(object): +    """ +    CommonCrypto API wrapper. +    """ +    _module_prefix = "cryptography.hazmat.bindings.commoncrypto." +    _modules = [ +        "cf", +        "common_digest", +        "common_hmac", +        "common_key_derivation", +        "common_cryptor", +        "secimport", +        "secitem", +        "seckey", +        "seckeychain", +        "sectransform", +    ] + +    ffi = build_ffi_for_binding( +        module_prefix=_module_prefix, +        modules=_modules, +        extra_link_args=[ +            "-framework", "Security", "-framework", "CoreFoundation" +        ], +    ) +    lib = None + +    def __init__(self): +        self._ensure_ffi_initialized() + +    @classmethod +    def _ensure_ffi_initialized(cls): +        if cls.lib is not None: +            return + +        cls.lib = load_library_for_binding( +            cls.ffi, +            module_prefix=cls._module_prefix, +            modules=cls._modules, +        ) diff --git a/src/cryptography/hazmat/bindings/commoncrypto/cf.py b/src/cryptography/hazmat/bindings/commoncrypto/cf.py new file mode 100644 index 00000000..671963a3 --- /dev/null +++ b/src/cryptography/hazmat/bindings/commoncrypto/cf.py @@ -0,0 +1,114 @@ +# 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 + +INCLUDES = """ +#include <CoreFoundation/CoreFoundation.h> +""" + +TYPES = """ +typedef bool Boolean; +typedef signed long OSStatus; +typedef unsigned char UInt8; +typedef uint32_t UInt32; + +typedef const void * CFAllocatorRef; +const CFAllocatorRef kCFAllocatorDefault; +typedef const void * CFDataRef; +typedef signed long long CFIndex; +typedef ... *CFStringRef; +typedef ... *CFArrayRef; +typedef ... *CFBooleanRef; +typedef ... *CFErrorRef; +typedef ... *CFNumberRef; +typedef ... *CFTypeRef; +typedef ... *CFDictionaryRef; +typedef ... *CFMutableDictionaryRef; +typedef struct { +    ...; +} CFDictionaryKeyCallBacks; +typedef struct { +    ...; +} CFDictionaryValueCallBacks; +typedef struct { +    ...; +} CFRange; + +typedef UInt32 CFStringEncoding; +enum { +    kCFStringEncodingASCII = 0x0600 +}; + +enum { +   kCFNumberSInt8Type = 1, +   kCFNumberSInt16Type = 2, +   kCFNumberSInt32Type = 3, +   kCFNumberSInt64Type = 4, +   kCFNumberFloat32Type = 5, +   kCFNumberFloat64Type = 6, +   kCFNumberCharType = 7, +   kCFNumberShortType = 8, +   kCFNumberIntType = 9, +   kCFNumberLongType = 10, +   kCFNumberLongLongType = 11, +   kCFNumberFloatType = 12, +   kCFNumberDoubleType = 13, +   kCFNumberCFIndexType = 14, +   kCFNumberNSIntegerType = 15, +   kCFNumberCGFloatType = 16, +   kCFNumberMaxType = 16 +}; +typedef int CFNumberType; + +const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks; +const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks; + +const CFBooleanRef kCFBooleanTrue; +const CFBooleanRef kCFBooleanFalse; +""" + +FUNCTIONS = """ +CFDataRef CFDataCreate(CFAllocatorRef, const UInt8 *, CFIndex); +CFStringRef CFStringCreateWithCString(CFAllocatorRef, const char *, +                                      CFStringEncoding); +CFDictionaryRef CFDictionaryCreate(CFAllocatorRef, const void **, +                                   const void **, CFIndex, +                                   const CFDictionaryKeyCallBacks *, +                                   const CFDictionaryValueCallBacks *); +CFMutableDictionaryRef CFDictionaryCreateMutable( +    CFAllocatorRef, +    CFIndex, +    const CFDictionaryKeyCallBacks *, +    const CFDictionaryValueCallBacks * +); +void CFDictionarySetValue(CFMutableDictionaryRef, const void *, const void *); +CFIndex CFArrayGetCount(CFArrayRef); +const void *CFArrayGetValueAtIndex(CFArrayRef, CFIndex); +CFIndex CFDataGetLength(CFDataRef); +void CFDataGetBytes(CFDataRef, CFRange, UInt8 *); +CFRange CFRangeMake(CFIndex, CFIndex); +void CFShow(CFTypeRef); +Boolean CFBooleanGetValue(CFBooleanRef); +CFNumberRef CFNumberCreate(CFAllocatorRef, CFNumberType, const void *); +void CFRelease(CFTypeRef); +CFTypeRef CFRetain(CFTypeRef); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/commoncrypto/common_cryptor.py b/src/cryptography/hazmat/bindings/commoncrypto/common_cryptor.py new file mode 100644 index 00000000..713bc566 --- /dev/null +++ b/src/cryptography/hazmat/bindings/commoncrypto/common_cryptor.py @@ -0,0 +1,110 @@ +# 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 + +INCLUDES = """ +#include <CommonCrypto/CommonCryptor.h> +""" + +TYPES = """ +enum { +    kCCAlgorithmAES128 = 0, +    kCCAlgorithmDES, +    kCCAlgorithm3DES, +    kCCAlgorithmCAST, +    kCCAlgorithmRC4, +    kCCAlgorithmRC2, +    kCCAlgorithmBlowfish +}; +typedef uint32_t CCAlgorithm; +enum { +    kCCSuccess = 0, +    kCCParamError = -4300, +    kCCBufferTooSmall = -4301, +    kCCMemoryFailure = -4302, +    kCCAlignmentError = -4303, +    kCCDecodeError = -4304, +    kCCUnimplemented = -4305 +}; +typedef int32_t CCCryptorStatus; +typedef uint32_t CCOptions; +enum { +    kCCEncrypt = 0, +    kCCDecrypt, +}; +typedef uint32_t CCOperation; +typedef ... *CCCryptorRef; + +enum { +    kCCModeOptionCTR_LE = 0x0001, +    kCCModeOptionCTR_BE = 0x0002 +}; + +typedef uint32_t CCModeOptions; + +enum { +    kCCModeECB = 1, +    kCCModeCBC = 2, +    kCCModeCFB = 3, +    kCCModeCTR = 4, +    kCCModeF8 = 5, +    kCCModeLRW = 6, +    kCCModeOFB = 7, +    kCCModeXTS = 8, +    kCCModeRC4 = 9, +    kCCModeCFB8 = 10, +    kCCModeGCM = 11 +}; +typedef uint32_t CCMode; +enum { +    ccNoPadding = 0, +    ccPKCS7Padding = 1, +}; +typedef uint32_t CCPadding; +""" + +FUNCTIONS = """ +CCCryptorStatus CCCryptorCreateWithMode(CCOperation, CCMode, CCAlgorithm, +                                        CCPadding, const void *, const void *, +                                        size_t, const void *, size_t, int, +                                        CCModeOptions, CCCryptorRef *); +CCCryptorStatus CCCryptorCreate(CCOperation, CCAlgorithm, CCOptions, +                                const void *, size_t, const void *, +                                CCCryptorRef *); +CCCryptorStatus CCCryptorUpdate(CCCryptorRef, const void *, size_t, void *, +                                size_t, size_t *); +CCCryptorStatus CCCryptorFinal(CCCryptorRef, void *, size_t, size_t *); +CCCryptorStatus CCCryptorRelease(CCCryptorRef); + +CCCryptorStatus CCCryptorGCMAddIV(CCCryptorRef, const void *, size_t); +CCCryptorStatus CCCryptorGCMAddAAD(CCCryptorRef, const void *, size_t); +CCCryptorStatus CCCryptorGCMEncrypt(CCCryptorRef, const void *, size_t, +                                    void *); +CCCryptorStatus CCCryptorGCMDecrypt(CCCryptorRef, const void *, size_t, +                                    void *); +CCCryptorStatus CCCryptorGCMFinal(CCCryptorRef, const void *, size_t *); +CCCryptorStatus CCCryptorGCMReset(CCCryptorRef); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +/* Not defined in the public header */ +enum { +    kCCModeGCM = 11 +}; +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/commoncrypto/common_digest.py b/src/cryptography/hazmat/bindings/commoncrypto/common_digest.py new file mode 100644 index 00000000..c59200cb --- /dev/null +++ b/src/cryptography/hazmat/bindings/commoncrypto/common_digest.py @@ -0,0 +1,69 @@ +# 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 + +INCLUDES = """ +#include <CommonCrypto/CommonDigest.h> +""" + +TYPES = """ +typedef uint32_t CC_LONG; +typedef uint64_t CC_LONG64; +typedef struct CC_MD5state_st { +    ...; +} CC_MD5_CTX; +typedef struct CC_SHA1state_st { +    ...; +} CC_SHA1_CTX; +typedef struct CC_SHA256state_st { +    ...; +} CC_SHA256_CTX; +typedef struct CC_SHA512state_st { +    ...; +} CC_SHA512_CTX; +""" + +FUNCTIONS = """ +int CC_MD5_Init(CC_MD5_CTX *); +int CC_MD5_Update(CC_MD5_CTX *, const void *, CC_LONG); +int CC_MD5_Final(unsigned char *, CC_MD5_CTX *); + +int CC_SHA1_Init(CC_SHA1_CTX *); +int CC_SHA1_Update(CC_SHA1_CTX *, const void *, CC_LONG); +int CC_SHA1_Final(unsigned char *, CC_SHA1_CTX *); + +int CC_SHA224_Init(CC_SHA256_CTX *); +int CC_SHA224_Update(CC_SHA256_CTX *, const void *, CC_LONG); +int CC_SHA224_Final(unsigned char *, CC_SHA256_CTX *); + +int CC_SHA256_Init(CC_SHA256_CTX *); +int CC_SHA256_Update(CC_SHA256_CTX *, const void *, CC_LONG); +int CC_SHA256_Final(unsigned char *, CC_SHA256_CTX *); + +int CC_SHA384_Init(CC_SHA512_CTX *); +int CC_SHA384_Update(CC_SHA512_CTX *, const void *, CC_LONG); +int CC_SHA384_Final(unsigned char *, CC_SHA512_CTX *); + +int CC_SHA512_Init(CC_SHA512_CTX *); +int CC_SHA512_Update(CC_SHA512_CTX *, const void *, CC_LONG); +int CC_SHA512_Final(unsigned char *, CC_SHA512_CTX *); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/commoncrypto/common_hmac.py b/src/cryptography/hazmat/bindings/commoncrypto/common_hmac.py new file mode 100644 index 00000000..4f54b62b --- /dev/null +++ b/src/cryptography/hazmat/bindings/commoncrypto/common_hmac.py @@ -0,0 +1,48 @@ +# 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 + +INCLUDES = """ +#include <CommonCrypto/CommonHMAC.h> +""" + +TYPES = """ +typedef struct { +    ...; +} CCHmacContext; +enum { +    kCCHmacAlgSHA1, +    kCCHmacAlgMD5, +    kCCHmacAlgSHA256, +    kCCHmacAlgSHA384, +    kCCHmacAlgSHA512, +    kCCHmacAlgSHA224 +}; +typedef uint32_t CCHmacAlgorithm; +""" + +FUNCTIONS = """ +void CCHmacInit(CCHmacContext *, CCHmacAlgorithm, const void *, size_t); +void CCHmacUpdate(CCHmacContext *, const void *, size_t); +void CCHmacFinal(CCHmacContext *, void *); + +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/commoncrypto/common_key_derivation.py b/src/cryptography/hazmat/bindings/commoncrypto/common_key_derivation.py new file mode 100644 index 00000000..e8cc03ef --- /dev/null +++ b/src/cryptography/hazmat/bindings/commoncrypto/common_key_derivation.py @@ -0,0 +1,50 @@ +# 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 + +INCLUDES = """ +#include <CommonCrypto/CommonKeyDerivation.h> +""" + +TYPES = """ +enum { +    kCCPBKDF2 = 2, +}; +typedef uint32_t CCPBKDFAlgorithm; +enum { +    kCCPRFHmacAlgSHA1 = 1, +    kCCPRFHmacAlgSHA224 = 2, +    kCCPRFHmacAlgSHA256 = 3, +    kCCPRFHmacAlgSHA384 = 4, +    kCCPRFHmacAlgSHA512 = 5, +}; +typedef uint32_t CCPseudoRandomAlgorithm; +typedef unsigned int uint; +""" + +FUNCTIONS = """ +int CCKeyDerivationPBKDF(CCPBKDFAlgorithm, const char *, size_t, +                         const uint8_t *, size_t, CCPseudoRandomAlgorithm, +                         uint, uint8_t *, size_t); +uint CCCalibratePBKDF(CCPBKDFAlgorithm, size_t, size_t, +                      CCPseudoRandomAlgorithm, size_t, uint32_t); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/commoncrypto/secimport.py b/src/cryptography/hazmat/bindings/commoncrypto/secimport.py new file mode 100644 index 00000000..add62c79 --- /dev/null +++ b/src/cryptography/hazmat/bindings/commoncrypto/secimport.py @@ -0,0 +1,95 @@ +# 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 + +INCLUDES = """ +#include <Security/SecImportExport.h> +""" + +TYPES = """ +typedef ... *SecAccessRef; + +CFStringRef kSecImportExportPassphrase; +CFStringRef kSecImportExportKeychain; +CFStringRef kSecImportExportAccess; + +typedef uint32_t SecExternalItemType; +enum { +    kSecItemTypeUnknown, +    kSecItemTypePrivateKey, +    kSecItemTypePublicKey, +    kSecItemTypeSessionKey, +    kSecItemTypeCertificate, +    kSecItemTypeAggregate +}; + + +typedef uint32_t SecExternalFormat; +enum { +    kSecFormatUnknown = 0, +    kSecFormatOpenSSL, +    kSecFormatSSH, +    kSecFormatBSAFE, +    kSecFormatRawKey, +    kSecFormatWrappedPKCS8, +    kSecFormatWrappedOpenSSL, +    kSecFormatWrappedSSH, +    kSecFormatWrappedLSH, +    kSecFormatX509Cert, +    kSecFormatPEMSequence, +    kSecFormatPKCS7, +    kSecFormatPKCS12, +    kSecFormatNetscapeCertSequence, +    kSecFormatSSHv2 +}; + +typedef uint32_t SecItemImportExportFlags; +enum { +    kSecKeyImportOnlyOne        = 0x00000001, +    kSecKeySecurePassphrase     = 0x00000002, +    kSecKeyNoAccessControl      = 0x00000004 +}; +typedef uint32_t SecKeyImportExportFlags; + +typedef struct { +    /* for import and export */ +    uint32_t version; +    SecKeyImportExportFlags  flags; +    CFTypeRef                passphrase; +    CFStringRef              alertTitle; +    CFStringRef              alertPrompt; + +    /* for import only */ +    SecAccessRef             accessRef; +    CFArrayRef               keyUsage; + +    CFArrayRef               keyAttributes; +} SecItemImportExportKeyParameters; +""" + +FUNCTIONS = """ +OSStatus SecItemImport(CFDataRef, CFStringRef, SecExternalFormat *, +                       SecExternalItemType *, SecItemImportExportFlags, +                       const SecItemImportExportKeyParameters *, +                       SecKeychainRef, CFArrayRef *); +OSStatus SecPKCS12Import(CFDataRef, CFDictionaryRef, CFArrayRef *); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/commoncrypto/secitem.py b/src/cryptography/hazmat/bindings/commoncrypto/secitem.py new file mode 100644 index 00000000..ac3dad3f --- /dev/null +++ b/src/cryptography/hazmat/bindings/commoncrypto/secitem.py @@ -0,0 +1,38 @@ +# 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 + +INCLUDES = """ +#include <Security/SecItem.h> +""" + +TYPES = """ +const CFTypeRef kSecAttrKeyType; +const CFTypeRef kSecAttrKeySizeInBits; +const CFTypeRef kSecAttrIsPermanent; +const CFTypeRef kSecAttrKeyTypeRSA; +const CFTypeRef kSecAttrKeyTypeDSA; +const CFTypeRef kSecUseKeychain; +""" + +FUNCTIONS = """ +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/commoncrypto/seckey.py b/src/cryptography/hazmat/bindings/commoncrypto/seckey.py new file mode 100644 index 00000000..5e4b6dac --- /dev/null +++ b/src/cryptography/hazmat/bindings/commoncrypto/seckey.py @@ -0,0 +1,35 @@ +# 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 + +INCLUDES = """ +#include <Security/SecKey.h> +""" + +TYPES = """ +typedef ... *SecKeyRef; +""" + +FUNCTIONS = """ +OSStatus SecKeyGeneratePair(CFDictionaryRef, SecKeyRef *, SecKeyRef *); +size_t SecKeyGetBlockSize(SecKeyRef); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/commoncrypto/seckeychain.py b/src/cryptography/hazmat/bindings/commoncrypto/seckeychain.py new file mode 100644 index 00000000..c045c347 --- /dev/null +++ b/src/cryptography/hazmat/bindings/commoncrypto/seckeychain.py @@ -0,0 +1,36 @@ +# 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 + +INCLUDES = """ +#include <Security/SecKeychain.h> +""" + +TYPES = """ +typedef ... *SecKeychainRef; +""" + +FUNCTIONS = """ +OSStatus SecKeychainCreate(const char *, UInt32, const void *, Boolean, +                           SecAccessRef, SecKeychainRef *); +OSStatus SecKeychainDelete(SecKeychainRef); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/commoncrypto/sectransform.py b/src/cryptography/hazmat/bindings/commoncrypto/sectransform.py new file mode 100644 index 00000000..d6dbc5f6 --- /dev/null +++ b/src/cryptography/hazmat/bindings/commoncrypto/sectransform.py @@ -0,0 +1,79 @@ +# 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 + +INCLUDES = """ +#include <Security/SecDigestTransform.h> +#include <Security/SecSignVerifyTransform.h> +#include <Security/SecEncryptTransform.h> +""" + +TYPES = """ +typedef ... *SecTransformRef; + +CFStringRef kSecImportExportPassphrase; +CFStringRef kSecImportExportKeychain; +CFStringRef kSecImportExportAccess; + +CFStringRef kSecEncryptionMode; +CFStringRef kSecEncryptKey; +CFStringRef kSecIVKey; +CFStringRef kSecModeCBCKey; +CFStringRef kSecModeCFBKey; +CFStringRef kSecModeECBKey; +CFStringRef kSecModeNoneKey; +CFStringRef kSecModeOFBKey; +CFStringRef kSecOAEPEncodingParametersAttributeName; +CFStringRef kSecPaddingKey; +CFStringRef kSecPaddingNoneKey; +CFStringRef kSecPaddingOAEPKey; +CFStringRef kSecPaddingPKCS1Key; +CFStringRef kSecPaddingPKCS5Key; +CFStringRef kSecPaddingPKCS7Key; + +const CFStringRef kSecTransformInputAttributeName; +const CFStringRef kSecTransformOutputAttributeName; +const CFStringRef kSecTransformDebugAttributeName; +const CFStringRef kSecTransformTransformName; +const CFStringRef kSecTransformAbortAttributeName; + +CFStringRef kSecInputIsAttributeName; +CFStringRef kSecInputIsPlainText; +CFStringRef kSecInputIsDigest; +CFStringRef kSecInputIsRaw; + +const CFStringRef kSecDigestTypeAttribute; +const CFStringRef kSecDigestLengthAttribute; +const CFStringRef kSecDigestMD5; +const CFStringRef kSecDigestSHA1; +const CFStringRef kSecDigestSHA2; +""" + +FUNCTIONS = """ +Boolean SecTransformSetAttribute(SecTransformRef, CFStringRef, CFTypeRef, +                                 CFErrorRef *); +SecTransformRef SecDecryptTransformCreate(SecKeyRef, CFErrorRef *); +SecTransformRef SecEncryptTransformCreate(SecKeyRef, CFErrorRef *); +SecTransformRef SecVerifyTransformCreate(SecKeyRef, CFDataRef, CFErrorRef *); +SecTransformRef SecSignTransformCreate(SecKeyRef, CFErrorRef *) ; +CFTypeRef SecTransformExecute(SecTransformRef, CFErrorRef *); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/__init__.py b/src/cryptography/hazmat/bindings/openssl/__init__.py new file mode 100644 index 00000000..2f420574 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/__init__.py @@ -0,0 +1,14 @@ +# 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 diff --git a/src/cryptography/hazmat/bindings/openssl/aes.py b/src/cryptography/hazmat/bindings/openssl/aes.py new file mode 100644 index 00000000..e4071523 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/aes.py @@ -0,0 +1,70 @@ +# 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 + +INCLUDES = """ +#include <openssl/aes.h> +""" + +TYPES = """ +static const int Cryptography_HAS_AES_WRAP; + +struct aes_key_st { +    ...; +}; +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); + +/* The ctr128_encrypt function is only useful in 0.9.8. You should use EVP for +   this in 1.0.0+. It is defined in macros because the function signature +   changed after 0.9.8 */ +void AES_ctr128_encrypt(const unsigned char *, unsigned char *, +                        const size_t, const AES_KEY *, +                        unsigned char[], unsigned char[], unsigned int *); + +""" + +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 = { +    "Cryptography_HAS_AES_WRAP": [ +        "AES_wrap_key", +        "AES_unwrap_key", +    ], +} diff --git a/src/cryptography/hazmat/bindings/openssl/asn1.py b/src/cryptography/hazmat/bindings/openssl/asn1.py new file mode 100644 index 00000000..2edfd2d8 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/asn1.py @@ -0,0 +1,152 @@ +# 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 + +INCLUDES = """ +#include <openssl/asn1.h> +""" + +TYPES = """ +/* + * TODO: This typedef is wrong. + * + * This is due to limitations of cffi. + * See https://bitbucket.org/cffi/cffi/issue/69 + * + * For another possible work-around (not used here because it involves more + * complicated use of the cffi API which falls outside the general pattern used + * by this package), see + * http://paste.pound-python.org/show/iJcTUMkKeBeS6yXpZWUU/ + * + * The work-around used here is to just be sure to declare a type that is at + * least as large as the real type.  Maciej explains: + * + * <fijal> I think you want to declare your value too large (e.g. long) + * <fijal> that way you'll never pass garbage + */ +typedef intptr_t time_t; + +typedef int ASN1_BOOLEAN; +typedef ... ASN1_INTEGER; + +struct asn1_string_st { +    int length; +    int type; +    unsigned char *data; +    long flags; +}; + +typedef struct asn1_string_st ASN1_OCTET_STRING; +typedef struct asn1_string_st ASN1_IA5STRING; +typedef ... ASN1_OBJECT; +typedef ... ASN1_STRING; +typedef ... ASN1_TYPE; +typedef ... ASN1_GENERALIZEDTIME; +typedef ... ASN1_ENUMERATED; +typedef ... ASN1_ITEM; +typedef ... ASN1_VALUE; + +typedef struct { +    ...; +} ASN1_TIME; +typedef ... ASN1_ITEM_EXP; + +typedef ... ASN1_UTCTIME; + +static const int V_ASN1_GENERALIZEDTIME; + +static const int MBSTRING_UTF8; +""" + +FUNCTIONS = """ +ASN1_OBJECT *ASN1_OBJECT_new(void); +void ASN1_OBJECT_free(ASN1_OBJECT *); + +/*  ASN1 OBJECT IDENTIFIER */ +ASN1_OBJECT *d2i_ASN1_OBJECT(ASN1_OBJECT **, const unsigned char **, long); +int i2d_ASN1_OBJECT(ASN1_OBJECT *, unsigned char **); + +/*  ASN1 STRING */ +ASN1_STRING *ASN1_STRING_new(void); +ASN1_STRING *ASN1_STRING_type_new(int); +void ASN1_STRING_free(ASN1_STRING *); +unsigned char *ASN1_STRING_data(ASN1_STRING *); +int ASN1_STRING_set(ASN1_STRING *, const void *, int); +int ASN1_STRING_type(ASN1_STRING *); +int ASN1_STRING_to_UTF8(unsigned char **, ASN1_STRING *); + +/*  ASN1 OCTET STRING */ +ASN1_OCTET_STRING *ASN1_OCTET_STRING_new(void); +void ASN1_OCTET_STRING_free(ASN1_OCTET_STRING *); +int ASN1_OCTET_STRING_set(ASN1_OCTET_STRING *, const unsigned char *, int); + +/*  ASN1 INTEGER */ +ASN1_INTEGER *ASN1_INTEGER_new(void); +void ASN1_INTEGER_free(ASN1_INTEGER *); +int ASN1_INTEGER_set(ASN1_INTEGER *, long); +int i2a_ASN1_INTEGER(BIO *, ASN1_INTEGER *); + +/*  ASN1 TIME */ +ASN1_TIME *ASN1_TIME_new(void); +void ASN1_TIME_free(ASN1_TIME *); +ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(ASN1_TIME *, +                                                   ASN1_GENERALIZEDTIME **); + +/*  ASN1 UTCTIME */ +int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *, time_t); + +/*  ASN1 GENERALIZEDTIME */ +int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *, const char *); +void ASN1_GENERALIZEDTIME_free(ASN1_GENERALIZEDTIME *); + +/*  ASN1 ENUMERATED */ +ASN1_ENUMERATED *ASN1_ENUMERATED_new(void); +void ASN1_ENUMERATED_free(ASN1_ENUMERATED *); +int ASN1_ENUMERATED_set(ASN1_ENUMERATED *, long); + +ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **, const unsigned char **, long, +                          const ASN1_ITEM *); +""" + +MACROS = """ +ASN1_TIME *M_ASN1_TIME_dup(void *); +const ASN1_ITEM *ASN1_ITEM_ptr(ASN1_ITEM_EXP *); + +/* These aren't macros these arguments are all const X on openssl > 1.0.x */ + +int ASN1_STRING_length(ASN1_STRING *); +ASN1_STRING *ASN1_STRING_dup(ASN1_STRING *); +int ASN1_STRING_cmp(ASN1_STRING *, ASN1_STRING *); + +ASN1_OCTET_STRING *ASN1_OCTET_STRING_dup(ASN1_OCTET_STRING *); +int ASN1_OCTET_STRING_cmp(ASN1_OCTET_STRING *, ASN1_OCTET_STRING *); + +ASN1_INTEGER *ASN1_INTEGER_dup(ASN1_INTEGER *); +int ASN1_INTEGER_cmp(ASN1_INTEGER *, ASN1_INTEGER *); +long ASN1_INTEGER_get(ASN1_INTEGER *); + +BIGNUM *ASN1_INTEGER_to_BN(ASN1_INTEGER *, BIGNUM *); +ASN1_INTEGER *BN_to_ASN1_INTEGER(BIGNUM *, ASN1_INTEGER *); + +/* These isn't a macro the arg is const on openssl 1.0.2+ */ +int ASN1_GENERALIZEDTIME_check(ASN1_GENERALIZEDTIME *); + +/* Not a macro, const on openssl 1.0 */ +int ASN1_STRING_set_default_mask_asc(char *); +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/bignum.py b/src/cryptography/hazmat/bindings/openssl/bignum.py new file mode 100644 index 00000000..1d944ee9 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/bignum.py @@ -0,0 +1,114 @@ +# 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 + +INCLUDES = """ +#include <openssl/bn.h> +""" + +TYPES = """ +typedef ... BN_CTX; +typedef ... BIGNUM; +/* + * TODO: This typedef is wrong. + * + * This is due to limitations of cffi. + * See https://bitbucket.org/cffi/cffi/issue/69 + * + * For another possible work-around (not used here because it involves more + * complicated use of the cffi API which falls outside the general pattern used + * by this package), see + * http://paste.pound-python.org/show/iJcTUMkKeBeS6yXpZWUU/ + * + * The work-around used here is to just be sure to declare a type that is at + * least as large as the real type.  Maciej explains: + * + * <fijal> I think you want to declare your value too large (e.g. long) + * <fijal> that way you'll never pass garbage + */ +typedef uintptr_t BN_ULONG; +""" + +FUNCTIONS = """ +BIGNUM *BN_new(void); +void BN_free(BIGNUM *); + +BN_CTX *BN_CTX_new(void); +void BN_CTX_free(BN_CTX *); + +void BN_CTX_start(BN_CTX *); +BIGNUM *BN_CTX_get(BN_CTX *); +void BN_CTX_end(BN_CTX *); + +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 *); +int BN_dec2bn(BIGNUM **, const char *); + +int BN_bn2bin(const BIGNUM *, unsigned char *); +BIGNUM *BN_bin2bn(const unsigned char *, int, BIGNUM *); + +int BN_num_bits(const BIGNUM *); + +int BN_cmp(const BIGNUM *, 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 *); + +int BN_set_bit(BIGNUM *, int); +int BN_clear_bit(BIGNUM *, int); + +int BN_is_bit_set(const BIGNUM *, int); + +int BN_mask_bits(BIGNUM *, int); +""" + +MACROS = """ +int BN_zero(BIGNUM *); +int BN_one(BIGNUM *); +int BN_mod(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); + +int BN_lshift(BIGNUM *, const BIGNUM *, int); +int BN_lshift1(BIGNUM *, BIGNUM *); + +int BN_rshift(BIGNUM *, BIGNUM *, int); +int BN_rshift1(BIGNUM *, BIGNUM *); +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py new file mode 100644 index 00000000..ff9039cf --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -0,0 +1,176 @@ +# 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 os +import sys +import threading + +from cryptography.hazmat.bindings.utils import ( +    build_ffi_for_binding, load_library_for_binding, +) + + +_OSX_PRE_INCLUDE = """ +#ifdef __APPLE__ +#include <AvailabilityMacros.h> +#define __ORIG_DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER \ +    DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER +#undef DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER +#define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER +#endif +""" + +_OSX_POST_INCLUDE = """ +#ifdef __APPLE__ +#undef DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER +#define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER \ +    __ORIG_DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER +#endif +""" + + +def _get_libraries(platform): +    # OpenSSL goes by a different library name on different operating systems. +    if platform != "win32": +        # In some circumstances, the order in which these libs are +        # specified on the linker command-line is significant; +        # libssl must come before libcrypto +        # (http://marc.info/?l=openssl-users&m=135361825921871) +        return ["ssl", "crypto"] +    else: +        link_type = os.environ.get("PYCA_WINDOWS_LINK_TYPE", "static") +        return _get_windows_libraries(link_type) + + +def _get_windows_libraries(link_type): +    if link_type == "dynamic": +        return ["libeay32", "ssleay32", "advapi32"] +    elif link_type == "static" or link_type == "": +        return ["libeay32mt", "ssleay32mt", "advapi32", +                "crypt32", "gdi32", "user32", "ws2_32"] +    else: +        raise ValueError( +            "PYCA_WINDOWS_LINK_TYPE must be 'static' or 'dynamic'" +        ) + + +class Binding(object): +    """ +    OpenSSL API wrapper. +    """ +    _module_prefix = "cryptography.hazmat.bindings.openssl." +    _modules = [ +        "aes", +        "asn1", +        "bignum", +        "bio", +        "cmac", +        "cms", +        "conf", +        "crypto", +        "dh", +        "dsa", +        "ec", +        "ecdh", +        "ecdsa", +        "engine", +        "err", +        "evp", +        "hmac", +        "nid", +        "objects", +        "opensslv", +        "osrandom_engine", +        "pem", +        "pkcs7", +        "pkcs12", +        "rand", +        "rsa", +        "ssl", +        "x509", +        "x509name", +        "x509v3", +        "x509_vfy" +    ] + +    _locks = None +    _lock_cb_handle = None +    _lock_init_lock = threading.Lock() + +    ffi = build_ffi_for_binding( +        module_prefix=_module_prefix, +        modules=_modules, +        pre_include=_OSX_PRE_INCLUDE, +        post_include=_OSX_POST_INCLUDE, +        libraries=_get_libraries(sys.platform) +    ) +    lib = None + +    def __init__(self): +        self._ensure_ffi_initialized() + +    @classmethod +    def _ensure_ffi_initialized(cls): +        if cls.lib is not None: +            return + +        cls.lib = load_library_for_binding( +            cls.ffi, +            cls._module_prefix, +            cls._modules, +        ) + +        res = cls.lib.Cryptography_add_osrandom_engine() +        assert res != 0 + +    @classmethod +    def init_static_locks(cls): +        with cls._lock_init_lock: +            cls._ensure_ffi_initialized() + +            if not cls._lock_cb_handle: +                cls._lock_cb_handle = cls.ffi.callback( +                    "void(int, int, const char *, int)", +                    cls._lock_cb +                ) + +            # Use Python's implementation if available, importing _ssl triggers +            # the setup for this. +            __import__("_ssl") + +            if cls.lib.CRYPTO_get_locking_callback() != cls.ffi.NULL: +                return + +            # If nothing else has setup a locking callback already, we set up +            # our own +            num_locks = cls.lib.CRYPTO_num_locks() +            cls._locks = [threading.Lock() for n in range(num_locks)] + +            cls.lib.CRYPTO_set_locking_callback(cls._lock_cb_handle) + +    @classmethod +    def _lock_cb(cls, mode, n, file, line): +        lock = cls._locks[n] + +        if mode & cls.lib.CRYPTO_LOCK: +            lock.acquire() +        elif mode & cls.lib.CRYPTO_UNLOCK: +            lock.release() +        else: +            raise RuntimeError( +                "Unknown lock mode {0}: lock={1}, file={2}, line={3}.".format( +                    mode, n, file, line +                ) +            ) diff --git a/src/cryptography/hazmat/bindings/openssl/bio.py b/src/cryptography/hazmat/bindings/openssl/bio.py new file mode 100644 index 00000000..cfe6034f --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/bio.py @@ -0,0 +1,181 @@ +# 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 + +INCLUDES = """ +#include <openssl/bio.h> +""" + +TYPES = """ +typedef struct bio_st BIO; +typedef void bio_info_cb(BIO *, int, const char *, int, long, long); +struct bio_method_st { +    int type; +    const char *name; +    int (*bwrite)(BIO *, const char *, int); +    int (*bread)(BIO *, char *, int); +    int (*bputs)(BIO *, const char *); +    int (*bgets)(BIO *, char*, int); +    long (*ctrl)(BIO *, int, long, void *); +    int (*create)(BIO *); +    int (*destroy)(BIO *); +    long (*callback_ctrl)(BIO *, int, bio_info_cb *); +    ...; +}; +typedef struct bio_method_st BIO_METHOD; +struct bio_st { +    BIO_METHOD *method; +    long (*callback)(struct bio_st*, int, const char*, int, long, long); +    char *cb_arg; +    int init; +    int shutdown; +    int flags; +    int retry_reason; +    int num; +    void *ptr; +    struct bio_st *next_bio; +    struct bio_st *prev_bio; +    int references; +    unsigned long num_read; +    unsigned long num_write; +    ...; +}; +typedef ... BUF_MEM; + +static const int BIO_TYPE_MEM; +static const int BIO_TYPE_FILE; +static const int BIO_TYPE_FD; +static const int BIO_TYPE_SOCKET; +static const int BIO_TYPE_CONNECT; +static const int BIO_TYPE_ACCEPT; +static const int BIO_TYPE_NULL; +static const int BIO_CLOSE; +static const int BIO_NOCLOSE; +static const int BIO_TYPE_SOURCE_SINK; +static const int BIO_CTRL_RESET; +static const int BIO_CTRL_EOF; +static const int BIO_CTRL_SET; +static const int BIO_CTRL_SET_CLOSE; +static const int BIO_CTRL_FLUSH; +static const int BIO_CTRL_DUP; +static const int BIO_CTRL_GET_CLOSE; +static const int BIO_CTRL_INFO; +static const int BIO_CTRL_GET; +static const int BIO_CTRL_PENDING; +static const int BIO_CTRL_WPENDING; +static const int BIO_C_FILE_SEEK; +static const int BIO_C_FILE_TELL; +static const int BIO_TYPE_NONE; +static const int BIO_TYPE_PROXY_CLIENT; +static const int BIO_TYPE_PROXY_SERVER; +static const int BIO_TYPE_NBIO_TEST; +static const int BIO_TYPE_BER; +static const int BIO_TYPE_BIO; +static const int BIO_TYPE_DESCRIPTOR; +static const int BIO_FLAGS_READ; +static const int BIO_FLAGS_WRITE; +static const int BIO_FLAGS_IO_SPECIAL; +static const int BIO_FLAGS_RWS; +static const int BIO_FLAGS_SHOULD_RETRY; +static const int BIO_TYPE_NULL_FILTER; +static const int BIO_TYPE_SSL; +static const int BIO_TYPE_MD; +static const int BIO_TYPE_BUFFER; +static const int BIO_TYPE_CIPHER; +static const int BIO_TYPE_BASE64; +static const int BIO_TYPE_FILTER; +""" + +FUNCTIONS = """ +BIO* BIO_new(BIO_METHOD *); +int BIO_set(BIO *, BIO_METHOD *); +int BIO_free(BIO *); +void BIO_vfree(BIO *); +void BIO_free_all(BIO *); +BIO *BIO_push(BIO *, BIO *); +BIO *BIO_pop(BIO *); +BIO *BIO_next(BIO *); +BIO *BIO_find_type(BIO *, int); +BIO_METHOD *BIO_s_mem(void); +BIO *BIO_new_mem_buf(void *, int); +BIO_METHOD *BIO_s_file(void); +BIO *BIO_new_file(const char *, const char *); +BIO *BIO_new_fp(FILE *, int); +BIO_METHOD *BIO_s_fd(void); +BIO *BIO_new_fd(int, int); +BIO_METHOD *BIO_s_socket(void); +BIO *BIO_new_socket(int, int); +BIO_METHOD *BIO_s_null(void); +long BIO_ctrl(BIO *, int, long, void *); +long BIO_callback_ctrl( +    BIO *, +    int, +    void (*)(struct bio_st *, int, const char *, int, long, long) +); +char *BIO_ptr_ctrl(BIO *, int, long); +long BIO_int_ctrl(BIO *, int, long, int); +size_t BIO_ctrl_pending(BIO *); +size_t BIO_ctrl_wpending(BIO *); +int BIO_read(BIO *, void *, int); +int BIO_gets(BIO *, char *, int); +int BIO_write(BIO *, const void *, int); +int BIO_puts(BIO *, const char *); +BIO_METHOD *BIO_f_null(void); +BIO_METHOD *BIO_f_buffer(void); +""" + +MACROS = """ +long BIO_set_fd(BIO *, long, int); +long BIO_get_fd(BIO *, char *); +long BIO_set_mem_eof_return(BIO *, int); +long BIO_get_mem_data(BIO *, char **); +long BIO_set_mem_buf(BIO *, BUF_MEM *, int); +long BIO_get_mem_ptr(BIO *, BUF_MEM **); +long BIO_set_fp(BIO *, FILE *, int); +long BIO_get_fp(BIO *, FILE **); +long BIO_read_filename(BIO *, char *); +long BIO_write_filename(BIO *, char *); +long BIO_append_filename(BIO *, char *); +long BIO_rw_filename(BIO *, char *); +int BIO_should_read(BIO *); +int BIO_should_write(BIO *); +int BIO_should_io_special(BIO *); +int BIO_retry_type(BIO *); +int BIO_should_retry(BIO *); +int BIO_reset(BIO *); +int BIO_seek(BIO *, int); +int BIO_tell(BIO *); +int BIO_flush(BIO *); +int BIO_eof(BIO *); +int BIO_set_close(BIO *,long); +int BIO_get_close(BIO *); +int BIO_pending(BIO *); +int BIO_wpending(BIO *); +int BIO_get_info_callback(BIO *, bio_info_cb **); +int BIO_set_info_callback(BIO *, bio_info_cb *); +long BIO_get_buffer_num_lines(BIO *); +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 = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/cmac.py b/src/cryptography/hazmat/bindings/openssl/cmac.py new file mode 100644 index 00000000..c8bcc824 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/cmac.py @@ -0,0 +1,65 @@ +# 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 + +INCLUDES = """ +#if OPENSSL_VERSION_NUMBER >= 0x10001000L +#include <openssl/cmac.h> +#endif +""" + +TYPES = """ +static const int Cryptography_HAS_CMAC; +typedef ... CMAC_CTX; +""" + +FUNCTIONS = """ +""" + +MACROS = """ +CMAC_CTX *CMAC_CTX_new(void); +int CMAC_Init(CMAC_CTX *, const void *, size_t, const EVP_CIPHER *, ENGINE *); +int CMAC_Update(CMAC_CTX *, const void *, size_t); +int CMAC_Final(CMAC_CTX *, unsigned char *, size_t *); +int CMAC_CTX_copy(CMAC_CTX *, const CMAC_CTX *); +void CMAC_CTX_free(CMAC_CTX *); +""" + +CUSTOMIZATIONS = """ +#if OPENSSL_VERSION_NUMBER < 0x10001000L + +static const long Cryptography_HAS_CMAC = 0; +typedef void CMAC_CTX; +CMAC_CTX *(*CMAC_CTX_new)(void) = NULL; +int (*CMAC_Init)(CMAC_CTX *, const void *, size_t, const EVP_CIPHER *, +    ENGINE *) = NULL; +int (*CMAC_Update)(CMAC_CTX *, const void *, size_t) = NULL; +int (*CMAC_Final)(CMAC_CTX *, unsigned char *, size_t *) = NULL; +int (*CMAC_CTX_copy)(CMAC_CTX *, const CMAC_CTX *) = NULL; +void (*CMAC_CTX_free)(CMAC_CTX *) = NULL; +#else +static const long Cryptography_HAS_CMAC = 1; +#endif +""" + +CONDITIONAL_NAMES = { +    "Cryptography_HAS_CMAC": [ +        "CMAC_CTX_new", +        "CMAC_Init", +        "CMAC_Update", +        "CMAC_Final", +        "CMAC_CTX_copy", +        "CMAC_CTX_free", +    ], +} diff --git a/src/cryptography/hazmat/bindings/openssl/cms.py b/src/cryptography/hazmat/bindings/openssl/cms.py new file mode 100644 index 00000000..cbf4b283 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/cms.py @@ -0,0 +1,100 @@ +# 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 + +INCLUDES = """ +#if !defined(OPENSSL_NO_CMS) && OPENSSL_VERSION_NUMBER >= 0x0090808fL +/* The next define should really be in the OpenSSL header, but it is missing. +   Failing to include this on Windows causes compilation failures. */ +#if defined(OPENSSL_SYS_WINDOWS) +#include <windows.h> +#endif +#include <openssl/cms.h> +#endif +""" + +TYPES = """ +static const long Cryptography_HAS_CMS; + +typedef ... CMS_ContentInfo; +typedef ... CMS_SignerInfo; +typedef ... CMS_CertificateChoices; +typedef ... CMS_RevocationInfoChoice; +typedef ... CMS_RecipientInfo; +typedef ... CMS_ReceiptRequest; +typedef ... CMS_Receipt; +""" + +FUNCTIONS = """ +""" + +MACROS = """ +BIO *BIO_new_CMS(BIO *, CMS_ContentInfo *); +int i2d_CMS_bio_stream(BIO *, CMS_ContentInfo *, BIO *, int); +int PEM_write_bio_CMS_stream(BIO *, CMS_ContentInfo *, BIO *, int); +int CMS_final(CMS_ContentInfo *, BIO *, BIO *, unsigned int); +CMS_ContentInfo *CMS_sign(X509 *, EVP_PKEY *, Cryptography_STACK_OF_X509 *, +                          BIO *, unsigned int); +int CMS_verify(CMS_ContentInfo *, Cryptography_STACK_OF_X509 *, X509_STORE *, +               BIO *, BIO *, unsigned int); +CMS_ContentInfo *CMS_encrypt(Cryptography_STACK_OF_X509 *, BIO *, +                             const EVP_CIPHER *, unsigned int); +int CMS_decrypt(CMS_ContentInfo *, EVP_PKEY *, X509 *, BIO *, BIO *, +                unsigned int); +CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *, X509 *, EVP_PKEY *, +                                const EVP_MD *, unsigned int); +""" + +CUSTOMIZATIONS = """ +#if !defined(OPENSSL_NO_CMS) && OPENSSL_VERSION_NUMBER >= 0x0090808fL +static const long Cryptography_HAS_CMS = 1; +#else +static const long Cryptography_HAS_CMS = 0; +typedef void CMS_ContentInfo; +typedef void CMS_SignerInfo; +typedef void CMS_CertificateChoices; +typedef void CMS_RevocationInfoChoice; +typedef void CMS_RecipientInfo; +typedef void CMS_ReceiptRequest; +typedef void CMS_Receipt; +BIO *(*BIO_new_CMS)(BIO *, CMS_ContentInfo *) = NULL; +int (*i2d_CMS_bio_stream)(BIO *, CMS_ContentInfo *, BIO *, int) = NULL; +int (*PEM_write_bio_CMS_stream)(BIO *, CMS_ContentInfo *, BIO *, int) = NULL; +int (*CMS_final)(CMS_ContentInfo *, BIO *, BIO *, unsigned int) = NULL; +CMS_ContentInfo *(*CMS_sign)(X509 *, EVP_PKEY *, Cryptography_STACK_OF_X509 *, +                             BIO *, unsigned int) = NULL; +int (*CMS_verify)(CMS_ContentInfo *, Cryptography_STACK_OF_X509 *, +                  X509_STORE *, BIO *, BIO *, unsigned int) = NULL; +CMS_ContentInfo *(*CMS_encrypt)(Cryptography_STACK_OF_X509 *, BIO *, +                                const EVP_CIPHER *, unsigned int) = NULL; +int (*CMS_decrypt)(CMS_ContentInfo *, EVP_PKEY *, X509 *, BIO *, BIO *, +                   unsigned int) = NULL; +CMS_SignerInfo *(*CMS_add1_signer)(CMS_ContentInfo *, X509 *, EVP_PKEY *, +                                   const EVP_MD *, unsigned int) = NULL; +#endif +""" + +CONDITIONAL_NAMES = { +    "Cryptography_HAS_CMS": [ +        "BIO_new_CMS", +        "i2d_CMS_bio_stream", +        "PEM_write_bio_CMS_stream", +        "CMS_final", +        "CMS_sign", +        "CMS_verify", +        "CMS_encrypt", +        "CMS_decrypt", +        "CMS_add1_signer", +    ] +} diff --git a/src/cryptography/hazmat/bindings/openssl/conf.py b/src/cryptography/hazmat/bindings/openssl/conf.py new file mode 100644 index 00000000..001a0707 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/conf.py @@ -0,0 +1,35 @@ +# 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 + +INCLUDES = """ +#include <openssl/conf.h> +""" + +TYPES = """ +typedef ... CONF; +""" + +FUNCTIONS = """ +void OPENSSL_config(const char *); +void OPENSSL_no_config(void); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/crypto.py b/src/cryptography/hazmat/bindings/openssl/crypto.py new file mode 100644 index 00000000..99e1a61d --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/crypto.py @@ -0,0 +1,67 @@ +# 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 + +INCLUDES = """ +#include <openssl/crypto.h> +""" + +TYPES = """ +typedef ... CRYPTO_THREADID; + +static const int SSLEAY_VERSION; +static const int SSLEAY_CFLAGS; +static const int SSLEAY_PLATFORM; +static const int SSLEAY_DIR; +static const int SSLEAY_BUILT_ON; +static const int CRYPTO_MEM_CHECK_ON; +static const int CRYPTO_MEM_CHECK_OFF; +static const int CRYPTO_MEM_CHECK_ENABLE; +static const int CRYPTO_MEM_CHECK_DISABLE; +static const int CRYPTO_LOCK; +static const int CRYPTO_UNLOCK; +static const int CRYPTO_READ; +static const int CRYPTO_WRITE; +static const int CRYPTO_LOCK_SSL; +""" + +FUNCTIONS = """ +unsigned long SSLeay(void); +const char *SSLeay_version(int); + +void CRYPTO_free(void *); +int CRYPTO_mem_ctrl(int); +int CRYPTO_is_mem_check_on(void); +void CRYPTO_mem_leaks(struct bio_st *); +void CRYPTO_cleanup_all_ex_data(void); +int CRYPTO_num_locks(void); +void CRYPTO_set_locking_callback(void(*)(int, int, const char *, int)); +void CRYPTO_set_id_callback(unsigned long (*)(void)); +unsigned long (*CRYPTO_get_id_callback(void))(void); +void (*CRYPTO_get_locking_callback(void))(int, int, const char *, int); +void CRYPTO_lock(int, int, const char *, int); + +void OPENSSL_free(void *); +""" + +MACROS = """ +void CRYPTO_add(int *, int, int); +void CRYPTO_malloc_init(void); +void CRYPTO_malloc_debug_init(void); +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/dh.py b/src/cryptography/hazmat/bindings/openssl/dh.py new file mode 100644 index 00000000..e2e8976e --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/dh.py @@ -0,0 +1,57 @@ +# 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 + +INCLUDES = """ +#include <openssl/dh.h> +""" + +TYPES = """ +typedef struct dh_st { +    /* Prime number (shared) */ +    BIGNUM *p; +    /* Generator of Z_p (shared) */ +    BIGNUM *g; +    /* Private DH value x */ +    BIGNUM *priv_key; +    /* Public DH value g^x */ +    BIGNUM *pub_key; +    ...; +} DH; +""" + +FUNCTIONS = """ +DH *DH_new(void); +void DH_free(DH *); +int DH_size(const DH *); +DH *DH_generate_parameters(int, int, void (*)(int, int, void *), void *); +int DH_check(const DH *, int *); +int DH_generate_key(DH *); +int DH_compute_key(unsigned char *, const BIGNUM *, DH *); +int DH_set_ex_data(DH *, int, void *); +void *DH_get_ex_data(DH *, int); +DH *d2i_DHparams(DH **, const unsigned char **, long); +int i2d_DHparams(const DH *, unsigned char **); +int DHparams_print_fp(FILE *, const DH *); +int DHparams_print(BIO *, const DH *); +""" + +MACROS = """ +int DH_generate_parameters_ex(DH *, int, int, BN_GENCB *); +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/dsa.py b/src/cryptography/hazmat/bindings/openssl/dsa.py new file mode 100644 index 00000000..c9aa8882 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/dsa.py @@ -0,0 +1,65 @@ +# 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 + +INCLUDES = """ +#include <openssl/dsa.h> +""" + +TYPES = """ +typedef struct dsa_st { +    /* Prime number (public) */ +    BIGNUM *p; +    /* Subprime (160-bit, q | p-1, public) */ +    BIGNUM *q; +    /* Generator of subgroup (public) */ +    BIGNUM *g; +    /* Private key x */ +    BIGNUM *priv_key; +    /* Public key y = g^x */ +    BIGNUM *pub_key; +    ...; +} DSA; +typedef struct { +    BIGNUM *r; +    BIGNUM *s; +} DSA_SIG; +""" + +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 *); +DSA_SIG *DSA_SIG_new(void); +void DSA_SIG_free(DSA_SIG *); +int i2d_DSA_SIG(const DSA_SIG *, unsigned char **); +DSA_SIG *d2i_DSA_SIG(DSA_SIG **, const unsigned char **, long); +int DSA_size(const DSA *); +int DSA_sign(int, const unsigned char *, int, unsigned char *, unsigned int *, +             DSA *); +int DSA_verify(int, const unsigned char *, int, const unsigned char *, int, +               DSA *); +""" + +MACROS = """ +int DSA_generate_parameters_ex(DSA *, int, unsigned char *, int, +                               int *, unsigned long *, BN_GENCB *); +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/ec.py b/src/cryptography/hazmat/bindings/openssl/ec.py new file mode 100644 index 00000000..26fc8ff0 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/ec.py @@ -0,0 +1,490 @@ +# 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 + +INCLUDES = """ +#ifndef OPENSSL_NO_EC +#include <openssl/ec.h> +#endif + +#include <openssl/obj_mac.h> +""" + +TYPES = """ +static const int Cryptography_HAS_EC; +static const int Cryptography_HAS_EC_1_0_1; +static const int Cryptography_HAS_EC_NISTP_64_GCC_128; +static const int Cryptography_HAS_EC2M; + +static const int OPENSSL_EC_NAMED_CURVE; + +typedef ... EC_KEY; +typedef ... EC_GROUP; +typedef ... EC_POINT; +typedef ... EC_METHOD; +typedef struct { +    int nid; +    const char *comment; +} EC_builtin_curve; +typedef enum { ... } point_conversion_form_t; +""" + +FUNCTIONS = """ +""" + +MACROS = """ +EC_GROUP *EC_GROUP_new(const EC_METHOD *); +void EC_GROUP_free(EC_GROUP *); +void EC_GROUP_clear_free(EC_GROUP *); + +EC_GROUP *EC_GROUP_new_curve_GFp( +    const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); +EC_GROUP *EC_GROUP_new_curve_GF2m( +    const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); +EC_GROUP *EC_GROUP_new_by_curve_name(int); + +int EC_GROUP_set_curve_GFp( +    EC_GROUP *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); +int EC_GROUP_get_curve_GFp( +    const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *); +int EC_GROUP_set_curve_GF2m( +    EC_GROUP *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); +int EC_GROUP_get_curve_GF2m( +    const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *); + +int EC_GROUP_get_degree(const EC_GROUP *); + +const EC_METHOD *EC_GROUP_method_of(const EC_GROUP *); +const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *); +int EC_GROUP_get_curve_name(const EC_GROUP *); + +size_t EC_get_builtin_curves(EC_builtin_curve *, size_t); + +void EC_KEY_free(EC_KEY *); + +int EC_KEY_get_flags(const EC_KEY *); +void EC_KEY_set_flags(EC_KEY *, int); +void EC_KEY_clear_flags(EC_KEY *, int); +EC_KEY *EC_KEY_new_by_curve_name(int); +EC_KEY *EC_KEY_copy(EC_KEY *, const EC_KEY *); +EC_KEY *EC_KEY_dup(const EC_KEY *); +int EC_KEY_up_ref(EC_KEY *); +const EC_GROUP *EC_KEY_get0_group(const EC_KEY *); +int EC_GROUP_get_order(const EC_GROUP *, BIGNUM *, BN_CTX *); +int EC_KEY_set_group(EC_KEY *, const EC_GROUP *); +const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *); +int EC_KEY_set_private_key(EC_KEY *, const BIGNUM *); +const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *); +int EC_KEY_set_public_key(EC_KEY *, const EC_POINT *); +unsigned int EC_KEY_get_enc_flags(const EC_KEY *); +void EC_KEY_set_enc_flags(EC_KEY *eckey, unsigned int); +point_conversion_form_t EC_KEY_get_conv_form(const EC_KEY *); +void EC_KEY_set_conv_form(EC_KEY *, point_conversion_form_t); +void *EC_KEY_get_key_method_data( +    EC_KEY *, +    void *(*)(void *), +    void (*)(void *), +    void (*)(void *) +); +void EC_KEY_insert_key_method_data( +    EC_KEY *, +    void *, +    void *(*)(void *), +    void (*)(void *), +    void (*)(void *) +); +void EC_KEY_set_asn1_flag(EC_KEY *, int); +int EC_KEY_precompute_mult(EC_KEY *, BN_CTX *); +int EC_KEY_generate_key(EC_KEY *); +int EC_KEY_check_key(const EC_KEY *); +int EC_KEY_set_public_key_affine_coordinates(EC_KEY *, BIGNUM *, BIGNUM *); + +EC_POINT *EC_POINT_new(const EC_GROUP *); +void EC_POINT_free(EC_POINT *); +void EC_POINT_clear_free(EC_POINT *); +int EC_POINT_copy(EC_POINT *, const EC_POINT *); +EC_POINT *EC_POINT_dup(const EC_POINT *, const EC_GROUP *); +const EC_METHOD *EC_POINT_method_of(const EC_POINT *); + +int EC_POINT_set_to_infinity(const EC_GROUP *, EC_POINT *); + +int EC_POINT_set_Jprojective_coordinates_GFp(const EC_GROUP *, EC_POINT *, +    const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); + +int EC_POINT_get_Jprojective_coordinates_GFp(const EC_GROUP *, +    const EC_POINT *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *); + +int EC_POINT_set_affine_coordinates_GFp(const EC_GROUP *, EC_POINT *, +    const BIGNUM *, const BIGNUM *, BN_CTX *); + +int EC_POINT_get_affine_coordinates_GFp(const EC_GROUP *, +    const EC_POINT *, BIGNUM *, BIGNUM *, BN_CTX *); + +int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *, EC_POINT *, +    const BIGNUM *, int, BN_CTX *); + +int EC_POINT_set_affine_coordinates_GF2m(const EC_GROUP *, EC_POINT *, +    const BIGNUM *, const BIGNUM *, BN_CTX *); + +int EC_POINT_get_affine_coordinates_GF2m(const EC_GROUP *, +    const EC_POINT *, BIGNUM *, BIGNUM *, BN_CTX *); + +int EC_POINT_set_compressed_coordinates_GF2m(const EC_GROUP *, EC_POINT *, +    const BIGNUM *, int, BN_CTX *); + +size_t EC_POINT_point2oct(const EC_GROUP *, const EC_POINT *, +    point_conversion_form_t, +    unsigned char *, size_t, BN_CTX *); + +int EC_POINT_oct2point(const EC_GROUP *, EC_POINT *, +    const unsigned char *, size_t, BN_CTX *); + +BIGNUM *EC_POINT_point2bn(const EC_GROUP *, const EC_POINT *, +    point_conversion_form_t form, BIGNUM *, BN_CTX *); + +EC_POINT *EC_POINT_bn2point(const EC_GROUP *, const BIGNUM *, +    EC_POINT *, BN_CTX *); + +char *EC_POINT_point2hex(const EC_GROUP *, const EC_POINT *, +    point_conversion_form_t form, BN_CTX *); + +EC_POINT *EC_POINT_hex2point(const EC_GROUP *, const char *, +    EC_POINT *, BN_CTX *); + +int EC_POINT_add(const EC_GROUP *, EC_POINT *, const EC_POINT *, +    const EC_POINT *, BN_CTX *); + +int EC_POINT_dbl(const EC_GROUP *, EC_POINT *, const EC_POINT *, BN_CTX *); +int EC_POINT_invert(const EC_GROUP *, EC_POINT *, BN_CTX *); +int EC_POINT_is_at_infinity(const EC_GROUP *, const EC_POINT *); +int EC_POINT_is_on_curve(const EC_GROUP *, const EC_POINT *, BN_CTX *); + +int EC_POINT_cmp( +    const EC_GROUP *, const EC_POINT *, const EC_POINT *, BN_CTX *); + +int EC_POINT_make_affine(const EC_GROUP *, EC_POINT *, BN_CTX *); +int EC_POINTs_make_affine(const EC_GROUP *, size_t, EC_POINT *[], BN_CTX *); + +int EC_POINTs_mul( +    const EC_GROUP *, EC_POINT *, const BIGNUM *, +    size_t, const EC_POINT *[], const BIGNUM *[], BN_CTX *); + +int EC_POINT_mul(const EC_GROUP *, EC_POINT *, const BIGNUM *, +    const EC_POINT *, const BIGNUM *, BN_CTX *); + +int EC_GROUP_precompute_mult(EC_GROUP *, BN_CTX *); +int EC_GROUP_have_precompute_mult(const EC_GROUP *); + +const EC_METHOD *EC_GFp_simple_method(); +const EC_METHOD *EC_GFp_mont_method(); +const EC_METHOD *EC_GFp_nist_method(); + +const EC_METHOD *EC_GFp_nistp224_method(); +const EC_METHOD *EC_GFp_nistp256_method(); +const EC_METHOD *EC_GFp_nistp521_method(); + +const EC_METHOD *EC_GF2m_simple_method(); + +int EC_METHOD_get_field_type(const EC_METHOD *); +""" + +CUSTOMIZATIONS = """ +#ifdef OPENSSL_NO_EC +static const long Cryptography_HAS_EC = 0; + +typedef void EC_KEY; +typedef void EC_GROUP; +typedef void EC_POINT; +typedef void EC_METHOD; +typedef struct { +    int nid; +    const char *comment; +} EC_builtin_curve; +typedef long point_conversion_form_t; + +static const int OPENSSL_EC_NAMED_CURVE = 0; + +void (*EC_KEY_free)(EC_KEY *) = NULL; +size_t (*EC_get_builtin_curves)(EC_builtin_curve *, size_t) = NULL; +EC_KEY *(*EC_KEY_new_by_curve_name)(int) = NULL; +EC_KEY *(*EC_KEY_copy)(EC_KEY *, const EC_KEY *) = NULL; +EC_KEY *(*EC_KEY_dup)(const EC_KEY *) = NULL; +int (*EC_KEY_up_ref)(EC_KEY *) = NULL; +const EC_GROUP *(*EC_KEY_get0_group)(const EC_KEY *) = NULL; +int (*EC_GROUP_get_order)(const EC_GROUP *, BIGNUM *, BN_CTX *) = NULL; +int (*EC_KEY_set_group)(EC_KEY *, const EC_GROUP *) = NULL; +const BIGNUM *(*EC_KEY_get0_private_key)(const EC_KEY *) = NULL; +int (*EC_KEY_set_private_key)(EC_KEY *, const BIGNUM *) = NULL; +const EC_POINT *(*EC_KEY_get0_public_key)(const EC_KEY *) = NULL; +int (*EC_KEY_set_public_key)(EC_KEY *, const EC_POINT *) = NULL; +unsigned int (*EC_KEY_get_enc_flags)(const EC_KEY *) = NULL; +void (*EC_KEY_set_enc_flags)(EC_KEY *eckey, unsigned int) = NULL; +point_conversion_form_t (*EC_KEY_get_conv_form)(const EC_KEY *) = NULL; +void (*EC_KEY_set_conv_form)(EC_KEY *, point_conversion_form_t) = NULL; +void *(*EC_KEY_get_key_method_data)( +    EC_KEY *, void *(*)(void *), void (*)(void *), void (*)(void *)) = NULL; +void (*EC_KEY_insert_key_method_data)( +    EC_KEY *, void *, +    void *(*)(void *), void (*)(void *), void (*)(void *)) = NULL; +void (*EC_KEY_set_asn1_flag)(EC_KEY *, int) = NULL; +int (*EC_KEY_precompute_mult)(EC_KEY *, BN_CTX *) = NULL; +int (*EC_KEY_generate_key)(EC_KEY *) = NULL; +int (*EC_KEY_check_key)(const EC_KEY *) = NULL; + +EC_GROUP *(*EC_GROUP_new)(const EC_METHOD *); +void (*EC_GROUP_free)(EC_GROUP *); +void (*EC_GROUP_clear_free)(EC_GROUP *); + +EC_GROUP *(*EC_GROUP_new_curve_GFp)( +    const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); + +EC_GROUP *(*EC_GROUP_new_by_curve_name)(int); + +int (*EC_GROUP_set_curve_GFp)( +    EC_GROUP *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); + +int (*EC_GROUP_get_curve_GFp)( +    const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *); + +int (*EC_GROUP_get_degree)(const EC_GROUP *) = NULL; + +const EC_METHOD *(*EC_GROUP_method_of)(const EC_GROUP *) = NULL; +const EC_POINT *(*EC_GROUP_get0_generator)(const EC_GROUP *) = NULL; +int (*EC_GROUP_get_curve_name)(const EC_GROUP *) = NULL; + +EC_POINT *(*EC_POINT_new)(const EC_GROUP *) = NULL; +void (*EC_POINT_free)(EC_POINT *) = NULL; +void (*EC_POINT_clear_free)(EC_POINT *) = NULL; +int (*EC_POINT_copy)(EC_POINT *, const EC_POINT *) = NULL; +EC_POINT *(*EC_POINT_dup)(const EC_POINT *, const EC_GROUP *) = NULL; +const EC_METHOD *(*EC_POINT_method_of)(const EC_POINT *) = NULL; +int (*EC_POINT_set_to_infinity)(const EC_GROUP *, EC_POINT *) = NULL; +int (*EC_POINT_set_Jprojective_coordinates_GFp)(const EC_GROUP *, EC_POINT *, +    const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *) = NULL; + +int (*EC_POINT_get_Jprojective_coordinates_GFp)(const EC_GROUP *, +    const EC_POINT *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *) = NULL; + +int (*EC_POINT_set_affine_coordinates_GFp)(const EC_GROUP *, EC_POINT *, +    const BIGNUM *, const BIGNUM *, BN_CTX *) = NULL; + +int (*EC_POINT_get_affine_coordinates_GFp)(const EC_GROUP *, +    const EC_POINT *, BIGNUM *, BIGNUM *, BN_CTX *) = NULL; + +int (*EC_POINT_set_compressed_coordinates_GFp)(const EC_GROUP *, EC_POINT *, +    const BIGNUM *, int, BN_CTX *) = NULL; + +size_t (*EC_POINT_point2oct)(const EC_GROUP *, const EC_POINT *, +    point_conversion_form_t, +    unsigned char *, size_t, BN_CTX *) = NULL; + +int (*EC_POINT_oct2point)(const EC_GROUP *, EC_POINT *, +    const unsigned char *, size_t, BN_CTX *) = NULL; + +BIGNUM *(*EC_POINT_point2bn)(const EC_GROUP *, const EC_POINT *, +    point_conversion_form_t form, BIGNUM *, BN_CTX *) = NULL; + +EC_POINT *(*EC_POINT_bn2point)(const EC_GROUP *, const BIGNUM *, +    EC_POINT *, BN_CTX *) = NULL; + +char *(*EC_POINT_point2hex)(const EC_GROUP *, const EC_POINT *, +    point_conversion_form_t form, BN_CTX *) = NULL; + +EC_POINT *(*EC_POINT_hex2point)(const EC_GROUP *, const char *, +    EC_POINT *, BN_CTX *) = NULL; + +int (*EC_POINT_add)(const EC_GROUP *, EC_POINT *, const EC_POINT *, +    const EC_POINT *, BN_CTX *) = NULL; + +int (*EC_POINT_dbl)(const EC_GROUP *, EC_POINT *, const EC_POINT *, +    BN_CTX *) = NULL; + +int (*EC_POINT_invert)(const EC_GROUP *, EC_POINT *, BN_CTX *) = NULL; +int (*EC_POINT_is_at_infinity)(const EC_GROUP *, const EC_POINT *) = NULL; + +int (*EC_POINT_is_on_curve)(const EC_GROUP *, const EC_POINT *, +    BN_CTX *) = NULL; + +int (*EC_POINT_cmp)( +    const EC_GROUP *, const EC_POINT *, const EC_POINT *, BN_CTX *) = NULL; + +int (*EC_POINT_make_affine)(const EC_GROUP *, EC_POINT *, BN_CTX *) = NULL; + +int (*EC_POINTs_make_affine)(const EC_GROUP *, size_t, EC_POINT *[], +    BN_CTX *) = NULL; + +int (*EC_POINTs_mul)( +    const EC_GROUP *, EC_POINT *, const BIGNUM *, +    size_t, const EC_POINT *[], const BIGNUM *[], BN_CTX *) = NULL; + +int (*EC_POINT_mul)(const EC_GROUP *, EC_POINT *, const BIGNUM *, +    const EC_POINT *, const BIGNUM *, BN_CTX *) = NULL; + +int (*EC_GROUP_precompute_mult)(EC_GROUP *, BN_CTX *) = NULL; +int (*EC_GROUP_have_precompute_mult)(const EC_GROUP *) = NULL; + +const EC_METHOD *(*EC_GFp_simple_method)() = NULL; +const EC_METHOD *(*EC_GFp_mont_method)() = NULL; +const EC_METHOD *(*EC_GFp_nist_method)() = NULL; + +int (*EC_METHOD_get_field_type)(const EC_METHOD *) = NULL; + +#else +static const long Cryptography_HAS_EC = 1; +#endif + +#if defined(OPENSSL_NO_EC) || OPENSSL_VERSION_NUMBER < 0x1000100f +static const long Cryptography_HAS_EC_1_0_1 = 0; + +int (*EC_KEY_get_flags)(const EC_KEY *) = NULL; +void (*EC_KEY_set_flags)(EC_KEY *, int) = NULL; +void (*EC_KEY_clear_flags)(EC_KEY *, int) = NULL; + +int (*EC_KEY_set_public_key_affine_coordinates)( +    EC_KEY *, BIGNUM *, BIGNUM *) = NULL; +#else +static const long Cryptography_HAS_EC_1_0_1 = 1; +#endif + + +#if defined(OPENSSL_NO_EC) || OPENSSL_VERSION_NUMBER < 0x1000100f || \ +    defined(OPENSSL_NO_EC_NISTP_64_GCC_128) +static const long Cryptography_HAS_EC_NISTP_64_GCC_128 = 0; + +const EC_METHOD *(*EC_GFp_nistp224_method)(void) = NULL; +const EC_METHOD *(*EC_GFp_nistp256_method)(void) = NULL; +const EC_METHOD *(*EC_GFp_nistp521_method)(void) = NULL; +#else +static const long Cryptography_HAS_EC_NISTP_64_GCC_128 = 1; +#endif + +#if defined(OPENSSL_NO_EC) || defined(OPENSSL_NO_EC2M) +static const long Cryptography_HAS_EC2M = 0; + +const EC_METHOD *(*EC_GF2m_simple_method)() = NULL; + +int (*EC_POINT_set_affine_coordinates_GF2m)(const EC_GROUP *, EC_POINT *, +    const BIGNUM *, const BIGNUM *, BN_CTX *) = NULL; + +int (*EC_POINT_get_affine_coordinates_GF2m)(const EC_GROUP *, +    const EC_POINT *, BIGNUM *, BIGNUM *, BN_CTX *) = NULL; + +int (*EC_POINT_set_compressed_coordinates_GF2m)(const EC_GROUP *, EC_POINT *, +    const BIGNUM *, int, BN_CTX *) = NULL; + +int (*EC_GROUP_set_curve_GF2m)( +    EC_GROUP *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); + +int (*EC_GROUP_get_curve_GF2m)( +    const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *); + +EC_GROUP *(*EC_GROUP_new_curve_GF2m)( +    const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); +#else +static const long Cryptography_HAS_EC2M = 1; +#endif +""" + +CONDITIONAL_NAMES = { +    "Cryptography_HAS_EC": [ +        "OPENSSL_EC_NAMED_CURVE", +        "EC_GROUP_new", +        "EC_GROUP_free", +        "EC_GROUP_clear_free", +        "EC_GROUP_new_curve_GFp", +        "EC_GROUP_new_by_curve_name", +        "EC_GROUP_set_curve_GFp", +        "EC_GROUP_get_curve_GFp", +        "EC_GROUP_method_of", +        "EC_GROUP_get0_generator", +        "EC_GROUP_get_curve_name", +        "EC_GROUP_get_degree", +        "EC_KEY_free", +        "EC_get_builtin_curves", +        "EC_KEY_new_by_curve_name", +        "EC_KEY_copy", +        "EC_KEY_dup", +        "EC_KEY_up_ref", +        "EC_KEY_set_group", +        "EC_KEY_get0_private_key", +        "EC_KEY_set_private_key", +        "EC_KEY_set_public_key", +        "EC_KEY_get_enc_flags", +        "EC_KEY_set_enc_flags", +        "EC_KEY_set_conv_form", +        "EC_KEY_get_key_method_data", +        "EC_KEY_insert_key_method_data", +        "EC_KEY_set_asn1_flag", +        "EC_KEY_precompute_mult", +        "EC_KEY_generate_key", +        "EC_KEY_check_key", +        "EC_POINT_new", +        "EC_POINT_free", +        "EC_POINT_clear_free", +        "EC_POINT_copy", +        "EC_POINT_dup", +        "EC_POINT_method_of", +        "EC_POINT_set_to_infinity", +        "EC_POINT_set_Jprojective_coordinates_GFp", +        "EC_POINT_get_Jprojective_coordinates_GFp", +        "EC_POINT_set_affine_coordinates_GFp", +        "EC_POINT_get_affine_coordinates_GFp", +        "EC_POINT_set_compressed_coordinates_GFp", +        "EC_POINT_point2oct", +        "EC_POINT_oct2point", +        "EC_POINT_point2bn", +        "EC_POINT_bn2point", +        "EC_POINT_point2hex", +        "EC_POINT_hex2point", +        "EC_POINT_add", +        "EC_POINT_dbl", +        "EC_POINT_invert", +        "EC_POINT_is_at_infinity", +        "EC_POINT_is_on_curve", +        "EC_POINT_cmp", +        "EC_POINT_make_affine", +        "EC_POINTs_make_affine", +        "EC_POINTs_mul", +        "EC_POINT_mul", +        "EC_GROUP_precompute_mult", +        "EC_GROUP_have_precompute_mult", +        "EC_GFp_simple_method", +        "EC_GFp_mont_method", +        "EC_GFp_nist_method", +        "EC_METHOD_get_field_type", +    ], + +    "Cryptography_HAS_EC_1_0_1": [ +        "EC_KEY_get_flags", +        "EC_KEY_set_flags", +        "EC_KEY_clear_flags", +        "EC_KEY_set_public_key_affine_coordinates", +    ], + +    "Cryptography_HAS_EC_NISTP_64_GCC_128": [ +        "EC_GFp_nistp224_method", +        "EC_GFp_nistp256_method", +        "EC_GFp_nistp521_method", +    ], + +    "Cryptography_HAS_EC2M": [ +        "EC_GF2m_simple_method", +        "EC_POINT_set_affine_coordinates_GF2m", +        "EC_POINT_get_affine_coordinates_GF2m", +        "EC_POINT_set_compressed_coordinates_GF2m", +        "EC_GROUP_set_curve_GF2m", +        "EC_GROUP_get_curve_GF2m", +        "EC_GROUP_new_curve_GF2m", +    ], +} diff --git a/src/cryptography/hazmat/bindings/openssl/ecdh.py b/src/cryptography/hazmat/bindings/openssl/ecdh.py new file mode 100644 index 00000000..960d46fb --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/ecdh.py @@ -0,0 +1,68 @@ +# 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 + +INCLUDES = """ +#ifndef OPENSSL_NO_ECDH +#include <openssl/ecdh.h> +#endif +""" + +TYPES = """ +static const int Cryptography_HAS_ECDH; +""" + +FUNCTIONS = """ +""" + +MACROS = """ +int ECDH_compute_key(void *, size_t, const EC_POINT *, EC_KEY *, +                     void *(*)(const void *, size_t, void *, size_t *)); + +int ECDH_get_ex_new_index(long, void *, CRYPTO_EX_new *, CRYPTO_EX_dup *, +                          CRYPTO_EX_free *); + +int ECDH_set_ex_data(EC_KEY *, int, void *); + +void *ECDH_get_ex_data(EC_KEY *, int); +""" + +CUSTOMIZATIONS = """ +#ifdef OPENSSL_NO_ECDH +static const long Cryptography_HAS_ECDH = 0; + +int (*ECDH_compute_key)(void *, size_t, const EC_POINT *, EC_KEY *, +                        void *(*)(const void *, size_t, void *, +                        size_t *)) = NULL; + +int (*ECDH_get_ex_new_index)(long, void *, CRYPTO_EX_new *, CRYPTO_EX_dup *, +                             CRYPTO_EX_free *) = NULL; + +int (*ECDH_set_ex_data)(EC_KEY *, int, void *) = NULL; + +void *(*ECDH_get_ex_data)(EC_KEY *, int) = NULL; + +#else +static const long Cryptography_HAS_ECDH = 1; +#endif +""" + +CONDITIONAL_NAMES = { +    "Cryptography_HAS_ECDH": [ +        "ECDH_compute_key", +        "ECDH_get_ex_new_index", +        "ECDH_set_ex_data", +        "ECDH_get_ex_data", +    ], +} diff --git a/src/cryptography/hazmat/bindings/openssl/ecdsa.py b/src/cryptography/hazmat/bindings/openssl/ecdsa.py new file mode 100644 index 00000000..bfa67206 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/ecdsa.py @@ -0,0 +1,130 @@ +# 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 + +INCLUDES = """ +#ifndef OPENSSL_NO_ECDSA +#include <openssl/ecdsa.h> +#endif +""" + +TYPES = """ +static const int Cryptography_HAS_ECDSA; + +typedef struct { +    BIGNUM *r; +    BIGNUM *s; +} ECDSA_SIG; + +typedef ... CRYPTO_EX_new; +typedef ... CRYPTO_EX_dup; +typedef ... CRYPTO_EX_free; +""" + +FUNCTIONS = """ +""" + +MACROS = """ +ECDSA_SIG *ECDSA_SIG_new(); +void ECDSA_SIG_free(ECDSA_SIG *); +int i2d_ECDSA_SIG(const ECDSA_SIG *, unsigned char **); +ECDSA_SIG *d2i_ECDSA_SIG(ECDSA_SIG **s, const unsigned char **, long); +ECDSA_SIG *ECDSA_do_sign(const unsigned char *, int, EC_KEY *); +ECDSA_SIG *ECDSA_do_sign_ex(const unsigned char *, int, const BIGNUM *, +                            const BIGNUM *, EC_KEY *); +int ECDSA_do_verify(const unsigned char *, int, const ECDSA_SIG *, EC_KEY*); +int ECDSA_sign_setup(EC_KEY *, BN_CTX *, BIGNUM **, BIGNUM **); +int ECDSA_sign(int, const unsigned char *, int, unsigned char *, +               unsigned int *, EC_KEY *); +int ECDSA_sign_ex(int, const unsigned char *, int dgstlen, unsigned char *, +                  unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *); +int ECDSA_verify(int, const unsigned char *, int, const unsigned char *, int, +                 EC_KEY *); +int ECDSA_size(const EC_KEY *); + +const ECDSA_METHOD* ECDSA_OpenSSL(); +void ECDSA_set_default_method(const ECDSA_METHOD *); +const ECDSA_METHOD* ECDSA_get_default_method(); +int ECDSA_get_ex_new_index(long, void *, CRYPTO_EX_new *, +                           CRYPTO_EX_dup *, CRYPTO_EX_free *); +int ECDSA_set_method(EC_KEY *, const ECDSA_METHOD *); +int ECDSA_set_ex_data(EC_KEY *, int, void *); +void *ECDSA_get_ex_data(EC_KEY *, int); +""" + +CUSTOMIZATIONS = """ +#ifdef OPENSSL_NO_ECDSA +static const long Cryptography_HAS_ECDSA = 0; + +typedef struct { +    BIGNUM *r; +    BIGNUM *s; +} ECDSA_SIG; + +ECDSA_SIG* (*ECDSA_SIG_new)() = NULL; +void (*ECDSA_SIG_free)(ECDSA_SIG *) = NULL; +int (*i2d_ECDSA_SIG)(const ECDSA_SIG *, unsigned char **) = NULL; +ECDSA_SIG* (*d2i_ECDSA_SIG)(ECDSA_SIG **s, const unsigned char **, +                            long) = NULL; +ECDSA_SIG* (*ECDSA_do_sign)(const unsigned char *, int, EC_KEY *eckey) = NULL; +ECDSA_SIG* (*ECDSA_do_sign_ex)(const unsigned char *, int, const BIGNUM *, +                               const BIGNUM *, EC_KEY *) = NULL; +int (*ECDSA_do_verify)(const unsigned char *, int, const ECDSA_SIG *, +                       EC_KEY*) = NULL; +int (*ECDSA_sign_setup)(EC_KEY *, BN_CTX *, BIGNUM **, BIGNUM **) = NULL; +int (*ECDSA_sign)(int, const unsigned char *, int, unsigned char *, +                  unsigned int *, EC_KEY *) = NULL; +int (*ECDSA_sign_ex)(int, const unsigned char *, int dgstlen, unsigned char *, +                     unsigned int *, const BIGNUM *, const BIGNUM *, +                     EC_KEY *) = NULL; +int (*ECDSA_verify)(int, const unsigned char *, int, const unsigned char *, +                    int, EC_KEY *) = NULL; +int (*ECDSA_size)(const EC_KEY *) = NULL; + +const ECDSA_METHOD* (*ECDSA_OpenSSL)() = NULL; +void (*ECDSA_set_default_method)(const ECDSA_METHOD *) = NULL; +const ECDSA_METHOD* (*ECDSA_get_default_method)() = NULL; +int (*ECDSA_set_method)(EC_KEY *, const ECDSA_METHOD *) = NULL; +int (*ECDSA_get_ex_new_index)(long, void *, CRYPTO_EX_new *, +                              CRYPTO_EX_dup *, CRYPTO_EX_free *) = NULL; +int (*ECDSA_set_ex_data)(EC_KEY *, int, void *) = NULL; +void* (*ECDSA_get_ex_data)(EC_KEY *, int) = NULL; +#else +static const long Cryptography_HAS_ECDSA = 1; +#endif +""" + +CONDITIONAL_NAMES = { +    "Cryptography_HAS_ECDSA": [ +        "ECDSA_SIG_new", +        "ECDSA_SIG_free", +        "i2d_ECDSA_SIG", +        "d2i_ECDSA_SIG", +        "ECDSA_do_sign", +        "ECDSA_do_sign_ex", +        "ECDSA_do_verify", +        "ECDSA_sign_setup", +        "ECDSA_sign", +        "ECDSA_sign_ex", +        "ECDSA_verify", +        "ECDSA_size", +        "ECDSA_OpenSSL", +        "ECDSA_set_default_method", +        "ECDSA_get_default_method", +        "ECDSA_set_method", +        "ECDSA_get_ex_new_index", +        "ECDSA_set_ex_data", +        "ECDSA_get_ex_data", +    ], +} diff --git a/src/cryptography/hazmat/bindings/openssl/engine.py b/src/cryptography/hazmat/bindings/openssl/engine.py new file mode 100644 index 00000000..364232e0 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/engine.py @@ -0,0 +1,165 @@ +# 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 + +INCLUDES = """ +#include <openssl/engine.h> +""" + +TYPES = """ +typedef ... ENGINE; +typedef ... RSA_METHOD; +typedef ... DSA_METHOD; +typedef ... ECDH_METHOD; +typedef ... ECDSA_METHOD; +typedef ... DH_METHOD; +typedef ... RAND_METHOD; +typedef ... STORE_METHOD; +typedef ... *ENGINE_GEN_INT_FUNC_PTR; +typedef ... *ENGINE_CTRL_FUNC_PTR; +typedef ... *ENGINE_LOAD_KEY_PTR; +typedef ... *ENGINE_CIPHERS_PTR; +typedef ... *ENGINE_DIGESTS_PTR; +typedef ... ENGINE_CMD_DEFN; +typedef ... UI_METHOD; + +static const unsigned int ENGINE_METHOD_RSA; +static const unsigned int ENGINE_METHOD_DSA; +static const unsigned int ENGINE_METHOD_RAND; +static const unsigned int ENGINE_METHOD_ECDH; +static const unsigned int ENGINE_METHOD_ECDSA; +static const unsigned int ENGINE_METHOD_CIPHERS; +static const unsigned int ENGINE_METHOD_DIGESTS; +static const unsigned int ENGINE_METHOD_STORE; +static const unsigned int ENGINE_METHOD_ALL; +static const unsigned int ENGINE_METHOD_NONE; +""" + +FUNCTIONS = """ +ENGINE *ENGINE_get_first(void); +ENGINE *ENGINE_get_last(void); +ENGINE *ENGINE_get_next(ENGINE *); +ENGINE *ENGINE_get_prev(ENGINE *); +int ENGINE_add(ENGINE *); +int ENGINE_remove(ENGINE *); +ENGINE *ENGINE_by_id(const char *); +int ENGINE_init(ENGINE *); +int ENGINE_finish(ENGINE *); +void ENGINE_load_openssl(void); +void ENGINE_load_dynamic(void); +void ENGINE_load_cryptodev(void); +void ENGINE_load_builtin_engines(void); +void ENGINE_cleanup(void); +ENGINE *ENGINE_get_default_RSA(void); +ENGINE *ENGINE_get_default_DSA(void); +ENGINE *ENGINE_get_default_ECDH(void); +ENGINE *ENGINE_get_default_ECDSA(void); +ENGINE *ENGINE_get_default_DH(void); +ENGINE *ENGINE_get_default_RAND(void); +ENGINE *ENGINE_get_cipher_engine(int); +ENGINE *ENGINE_get_digest_engine(int); +int ENGINE_set_default_RSA(ENGINE *); +int ENGINE_set_default_DSA(ENGINE *); +int ENGINE_set_default_ECDH(ENGINE *); +int ENGINE_set_default_ECDSA(ENGINE *); +int ENGINE_set_default_DH(ENGINE *); +int ENGINE_set_default_RAND(ENGINE *); +int ENGINE_set_default_ciphers(ENGINE *); +int ENGINE_set_default_digests(ENGINE *); +int ENGINE_set_default_string(ENGINE *, const char *); +int ENGINE_set_default(ENGINE *, unsigned int); +unsigned int ENGINE_get_table_flags(void); +void ENGINE_set_table_flags(unsigned int); +int ENGINE_register_RSA(ENGINE *); +void ENGINE_unregister_RSA(ENGINE *); +void ENGINE_register_all_RSA(void); +int ENGINE_register_DSA(ENGINE *); +void ENGINE_unregister_DSA(ENGINE *); +void ENGINE_register_all_DSA(void); +int ENGINE_register_ECDH(ENGINE *); +void ENGINE_unregister_ECDH(ENGINE *); +void ENGINE_register_all_ECDH(void); +int ENGINE_register_ECDSA(ENGINE *); +void ENGINE_unregister_ECDSA(ENGINE *); +void ENGINE_register_all_ECDSA(void); +int ENGINE_register_DH(ENGINE *); +void ENGINE_unregister_DH(ENGINE *); +void ENGINE_register_all_DH(void); +int ENGINE_register_RAND(ENGINE *); +void ENGINE_unregister_RAND(ENGINE *); +void ENGINE_register_all_RAND(void); +int ENGINE_register_STORE(ENGINE *); +void ENGINE_unregister_STORE(ENGINE *); +void ENGINE_register_all_STORE(void); +int ENGINE_register_ciphers(ENGINE *); +void ENGINE_unregister_ciphers(ENGINE *); +void ENGINE_register_all_ciphers(void); +int ENGINE_register_digests(ENGINE *); +void ENGINE_unregister_digests(ENGINE *); +void ENGINE_register_all_digests(void); +int ENGINE_register_complete(ENGINE *); +int ENGINE_register_all_complete(void); +int ENGINE_ctrl(ENGINE *, int, long, void *, void (*)(void)); +int ENGINE_cmd_is_executable(ENGINE *, int); +int ENGINE_ctrl_cmd(ENGINE *, const char *, long, void *, void (*)(void), int); +int ENGINE_ctrl_cmd_string(ENGINE *, const char *, const char *, int); + +ENGINE *ENGINE_new(void); +int ENGINE_free(ENGINE *); +int ENGINE_up_ref(ENGINE *); +int ENGINE_set_id(ENGINE *, const char *); +int ENGINE_set_name(ENGINE *, const char *); +int ENGINE_set_RSA(ENGINE *, const RSA_METHOD *); +int ENGINE_set_DSA(ENGINE *, const DSA_METHOD *); +int ENGINE_set_ECDH(ENGINE *, const ECDH_METHOD *); +int ENGINE_set_ECDSA(ENGINE *, const ECDSA_METHOD *); +int ENGINE_set_DH(ENGINE *, const DH_METHOD *); +int ENGINE_set_RAND(ENGINE *, const RAND_METHOD *); +int ENGINE_set_STORE(ENGINE *, const STORE_METHOD *); +int ENGINE_set_destroy_function(ENGINE *, ENGINE_GEN_INT_FUNC_PTR); +int ENGINE_set_init_function(ENGINE *, ENGINE_GEN_INT_FUNC_PTR); +int ENGINE_set_finish_function(ENGINE *, ENGINE_GEN_INT_FUNC_PTR); +int ENGINE_set_ctrl_function(ENGINE *, ENGINE_CTRL_FUNC_PTR); +int ENGINE_set_load_privkey_function(ENGINE *, ENGINE_LOAD_KEY_PTR); +int ENGINE_set_load_pubkey_function(ENGINE *, ENGINE_LOAD_KEY_PTR); +int ENGINE_set_ciphers(ENGINE *, ENGINE_CIPHERS_PTR); +int ENGINE_set_digests(ENGINE *, ENGINE_DIGESTS_PTR); +int ENGINE_set_flags(ENGINE *, int); +int ENGINE_set_cmd_defns(ENGINE *, const ENGINE_CMD_DEFN *); +const char *ENGINE_get_id(const ENGINE *); +const char *ENGINE_get_name(const ENGINE *); +const RSA_METHOD *ENGINE_get_RSA(const ENGINE *); +const DSA_METHOD *ENGINE_get_DSA(const ENGINE *); +const ECDH_METHOD *ENGINE_get_ECDH(const ENGINE *); +const ECDSA_METHOD *ENGINE_get_ECDSA(const ENGINE *); +const DH_METHOD *ENGINE_get_DH(const ENGINE *); +const RAND_METHOD *ENGINE_get_RAND(const ENGINE *); +const STORE_METHOD *ENGINE_get_STORE(const ENGINE *); + +const EVP_CIPHER *ENGINE_get_cipher(ENGINE *, int); +const EVP_MD *ENGINE_get_digest(ENGINE *, int); +int ENGINE_get_flags(const ENGINE *); +const ENGINE_CMD_DEFN *ENGINE_get_cmd_defns(const ENGINE *); +EVP_PKEY *ENGINE_load_private_key(ENGINE *, const char *, UI_METHOD *, void *); +EVP_PKEY *ENGINE_load_public_key(ENGINE *, const char *, UI_METHOD *, void *); +void ENGINE_add_conf_module(void); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/err.py b/src/cryptography/hazmat/bindings/openssl/err.py new file mode 100644 index 00000000..4e44a2eb --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/err.py @@ -0,0 +1,365 @@ +# 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 + +INCLUDES = """ +#include <openssl/err.h> +""" + +TYPES = """ +static const int Cryptography_HAS_REMOVE_THREAD_STATE; +static const int Cryptography_HAS_098H_ERROR_CODES; +static const int Cryptography_HAS_098C_CAMELLIA_CODES; +static const int Cryptography_HAS_EC_CODES; +static const int Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR; + +struct ERR_string_data_st { +    unsigned long error; +    const char *string; +}; +typedef struct ERR_string_data_st ERR_STRING_DATA; + +static const int ERR_LIB_EVP; +static const int ERR_LIB_EC; +static const int ERR_LIB_PEM; +static const int ERR_LIB_ASN1; +static const int ERR_LIB_RSA; +static const int ERR_LIB_PKCS12; + +static const int ASN1_F_ASN1_ENUMERATED_TO_BN; +static const int ASN1_F_ASN1_EX_C2I; +static const int ASN1_F_ASN1_FIND_END; +static const int ASN1_F_ASN1_GENERALIZEDTIME_SET; +static const int ASN1_F_ASN1_GENERATE_V3; +static const int ASN1_F_ASN1_GET_OBJECT; +static const int ASN1_F_ASN1_ITEM_I2D_FP; +static const int ASN1_F_ASN1_ITEM_PACK; +static const int ASN1_F_ASN1_ITEM_SIGN; +static const int ASN1_F_ASN1_ITEM_UNPACK; +static const int ASN1_F_ASN1_ITEM_VERIFY; +static const int ASN1_F_ASN1_MBSTRING_NCOPY; +static const int ASN1_F_ASN1_TEMPLATE_EX_D2I; +static const int ASN1_F_ASN1_TEMPLATE_NEW; +static const int ASN1_F_ASN1_TEMPLATE_NOEXP_D2I; +static const int ASN1_F_ASN1_TIME_SET; +static const int ASN1_F_ASN1_TYPE_GET_INT_OCTETSTRING; +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_BITSTR_CB; +static const int ASN1_F_BN_TO_ASN1_ENUMERATED; +static const int ASN1_F_BN_TO_ASN1_INTEGER; +static const int ASN1_F_D2I_ASN1_TYPE_BYTES; +static const int ASN1_F_D2I_ASN1_UINTEGER; +static const int ASN1_F_D2I_ASN1_UTCTIME; +static const int ASN1_F_D2I_NETSCAPE_RSA; +static const int ASN1_F_D2I_NETSCAPE_RSA_2; +static const int ASN1_F_D2I_PRIVATEKEY; +static const int ASN1_F_D2I_X509; +static const int ASN1_F_D2I_X509_CINF; +static const int ASN1_F_D2I_X509_PKEY; +static const int ASN1_F_I2D_ASN1_SET; +static const int ASN1_F_I2D_ASN1_TIME; +static const int ASN1_F_I2D_DSA_PUBKEY; +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_X509_CINF_NEW; + +static const int ASN1_R_BOOLEAN_IS_WRONG_LENGTH; +static const int ASN1_R_BUFFER_TOO_SMALL; +static const int ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER; +static const int ASN1_R_DATA_IS_WRONG; +static const int ASN1_R_DECODE_ERROR; +static const int ASN1_R_DECODING_ERROR; +static const int ASN1_R_DEPTH_EXCEEDED; +static const int ASN1_R_ENCODE_ERROR; +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_MATCHING_CHOICE_TYPE; +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; +static const int ASN1_R_UNKNOWN_TAG; +static const int ASN1_R_UNKOWN_FORMAT; +static const int ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE; +static const int ASN1_R_UNSUPPORTED_ENCRYPTION_ALGORITHM; +static const int ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE; +static const int ASN1_R_UNSUPPORTED_TYPE; +static const int ASN1_R_WRONG_TAG; +static const int ASN1_R_WRONG_TYPE; + +static const int EVP_F_AES_INIT_KEY; +static const int EVP_F_D2I_PKEY; +static const int EVP_F_DSA_PKEY2PKCS8; +static const int EVP_F_DSAPKEY2PKCS8; +static const int EVP_F_ECDSA_PKEY2PKCS8; +static const int EVP_F_ECKEY_PKEY2PKCS8; +static const int EVP_F_EVP_CIPHER_CTX_CTRL; +static const int EVP_F_EVP_CIPHER_CTX_SET_KEY_LENGTH; +static const int EVP_F_EVP_CIPHERINIT_EX; +static const int EVP_F_EVP_DECRYPTFINAL_EX; +static const int EVP_F_EVP_DIGESTINIT_EX; +static const int EVP_F_EVP_ENCRYPTFINAL_EX; +static const int EVP_F_EVP_MD_CTX_COPY_EX; +static const int EVP_F_EVP_OPENINIT; +static const int EVP_F_EVP_PBE_ALG_ADD; +static const int EVP_F_EVP_PBE_CIPHERINIT; +static const int EVP_F_EVP_PKCS82PKEY; +static const int EVP_F_EVP_PKEY2PKCS8_BROKEN; +static const int EVP_F_EVP_PKEY_COPY_PARAMETERS; +static const int EVP_F_EVP_PKEY_DECRYPT; +static const int EVP_F_EVP_PKEY_ENCRYPT; +static const int EVP_F_EVP_PKEY_GET1_DH; +static const int EVP_F_EVP_PKEY_GET1_DSA; +static const int EVP_F_EVP_PKEY_GET1_ECDSA; +static const int EVP_F_EVP_PKEY_GET1_EC_KEY; +static const int EVP_F_EVP_PKEY_GET1_RSA; +static const int EVP_F_EVP_PKEY_NEW; +static const int EVP_F_EVP_RIJNDAEL; +static const int EVP_F_EVP_SIGNFINAL; +static const int EVP_F_EVP_VERIFYFINAL; +static const int EVP_F_PKCS5_PBE_KEYIVGEN; +static const int EVP_F_PKCS5_V2_PBE_KEYIVGEN; +static const int EVP_F_PKCS8_SET_BROKEN; +static const int EVP_F_RC2_MAGIC_TO_METH; +static const int EVP_F_RC5_CTRL; + +static const int EVP_R_AES_KEY_SETUP_FAILED; +static const int EVP_R_ASN1_LIB; +static const int EVP_R_BAD_BLOCK_LENGTH; +static const int EVP_R_BAD_DECRYPT; +static const int EVP_R_BAD_KEY_LENGTH; +static const int EVP_R_BN_DECODE_ERROR; +static const int EVP_R_BN_PUBKEY_ERROR; +static const int EVP_R_CIPHER_PARAMETER_ERROR; +static const int EVP_R_CTRL_NOT_IMPLEMENTED; +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_ENCODE_ERROR; +static const int EVP_R_INITIALIZATION_ERROR; +static const int EVP_R_INPUT_NOT_INITIALIZED; +static const int EVP_R_INVALID_KEY_LENGTH; +static const int EVP_R_IV_TOO_LARGE; +static const int EVP_R_KEYGEN_FAILURE; +static const int EVP_R_MISSING_PARAMETERS; +static const int EVP_R_NO_CIPHER_SET; +static const int EVP_R_NO_DIGEST_SET; +static const int EVP_R_NO_DSA_PARAMETERS; +static const int EVP_R_NO_SIGN_FUNCTION_CONFIGURED; +static const int EVP_R_NO_VERIFY_FUNCTION_CONFIGURED; +static const int EVP_R_PKCS8_UNKNOWN_BROKEN_TYPE; +static const int EVP_R_PUBLIC_KEY_NOT_RSA; +static const int EVP_R_UNKNOWN_PBE_ALGORITHM; +static const int EVP_R_UNSUPORTED_NUMBER_OF_ROUNDS; +static const int EVP_R_UNSUPPORTED_CIPHER; +static const int EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION; +static const int EVP_R_UNSUPPORTED_KEYLENGTH; +static const int EVP_R_UNSUPPORTED_SALT_TYPE; +static const int EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM; +static const int EVP_R_WRONG_FINAL_BLOCK_LENGTH; +static const int EVP_R_WRONG_PUBLIC_KEY_TYPE; + +static const int EC_F_EC_GROUP_NEW_BY_CURVE_NAME; + +static const int EC_R_UNKNOWN_GROUP; + +static const int PEM_F_D2I_PKCS8PRIVATEKEY_BIO; +static const int PEM_F_D2I_PKCS8PRIVATEKEY_FP; +static const int PEM_F_DO_PK8PKEY; +static const int PEM_F_DO_PK8PKEY_FP; +static const int PEM_F_LOAD_IV; +static const int PEM_F_PEM_ASN1_READ; +static const int PEM_F_PEM_ASN1_READ_BIO; +static const int PEM_F_PEM_ASN1_WRITE; +static const int PEM_F_PEM_ASN1_WRITE_BIO; +static const int PEM_F_PEM_DEF_CALLBACK; +static const int PEM_F_PEM_DO_HEADER; +static const int PEM_F_PEM_F_PEM_WRITE_PKCS8PRIVATEKEY; +static const int PEM_F_PEM_GET_EVP_CIPHER_INFO; +static const int PEM_F_PEM_PK8PKEY; +static const int PEM_F_PEM_READ; +static const int PEM_F_PEM_READ_BIO; +static const int PEM_F_PEM_READ_BIO_PRIVATEKEY; +static const int PEM_F_PEM_READ_PRIVATEKEY; +static const int PEM_F_PEM_SEALFINAL; +static const int PEM_F_PEM_SEALINIT; +static const int PEM_F_PEM_SIGNFINAL; +static const int PEM_F_PEM_WRITE; +static const int PEM_F_PEM_WRITE_BIO; +static const int PEM_F_PEM_X509_INFO_READ; +static const int PEM_F_PEM_X509_INFO_READ_BIO; +static const int PEM_F_PEM_X509_INFO_WRITE_BIO; + +static const int PEM_R_BAD_BASE64_DECODE; +static const int PEM_R_BAD_DECRYPT; +static const int PEM_R_BAD_END_LINE; +static const int PEM_R_BAD_IV_CHARS; +static const int PEM_R_BAD_PASSWORD_READ; +static const int PEM_R_ERROR_CONVERTING_PRIVATE_KEY; +static const int PEM_R_NO_START_LINE; +static const int PEM_R_NOT_DEK_INFO; +static const int PEM_R_NOT_ENCRYPTED; +static const int PEM_R_NOT_PROC_TYPE; +static const int PEM_R_PROBLEMS_GETTING_PASSWORD; +static const int PEM_R_PUBLIC_KEY_NO_RSA; +static const int PEM_R_READ_KEY; +static const int PEM_R_SHORT_HEADER; +static const int PEM_R_UNSUPPORTED_CIPHER; +static const int PEM_R_UNSUPPORTED_ENCRYPTION; + +static const int PKCS12_F_PKCS12_PBE_CRYPT; + +static const int PKCS12_R_PKCS12_CIPHERFINAL_ERROR; + +static const int RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE; +static const int RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY; +static const int RSA_R_BLOCK_TYPE_IS_NOT_01; +static const int RSA_R_BLOCK_TYPE_IS_NOT_02; +static const int RSA_R_PKCS_DECODING_ERROR; +""" + +FUNCTIONS = """ +void ERR_load_crypto_strings(void); +void ERR_load_SSL_strings(void); +void ERR_free_strings(void); +char* ERR_error_string(unsigned long, char *); +void ERR_error_string_n(unsigned long, char *, size_t); +const char* ERR_lib_error_string(unsigned long); +const char* ERR_func_error_string(unsigned long); +const char* ERR_reason_error_string(unsigned long); +void ERR_print_errors(BIO *); +void ERR_print_errors_fp(FILE *); +unsigned long ERR_get_error(void); +unsigned long ERR_peek_error(void); +unsigned long ERR_peek_last_error(void); +unsigned long ERR_get_error_line(const char **, int *); +unsigned long ERR_peek_error_line(const char **, int *); +unsigned long ERR_peek_last_error_line(const char **, int *); +unsigned long ERR_get_error_line_data(const char **, int *, +                                      const char **, int *); +unsigned long ERR_peek_error_line_data(const char **, +                                       int *, const char **, int *); +unsigned long ERR_peek_last_error_line_data(const char **, +                                            int *, const char **, int *); +void ERR_put_error(int, int, int, const char *, int); +void ERR_add_error_data(int, ...); +int ERR_get_next_error_library(void); +""" + +MACROS = """ +unsigned long ERR_PACK(int, int, int); +int ERR_GET_LIB(unsigned long); +int ERR_GET_FUNC(unsigned long); +int ERR_GET_REASON(unsigned long); +int ERR_FATAL_ERROR(unsigned long); +/* introduced in 1.0.0 so we have to handle this specially to continue + * 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; +/* These were added in OpenSSL 0.9.8c. */ +static const int EVP_F_CAMELLIA_INIT_KEY; +static const int EVP_R_CAMELLIA_KEY_SETUP_FAILED; +""" + +CUSTOMIZATIONS = """ +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +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 *) = 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 + +/* OpenSSL 0.9.8c+ */ +#ifdef EVP_F_CAMELLIA_INIT_KEY +static const long Cryptography_HAS_098C_CAMELLIA_CODES = 1; +#else +static const long Cryptography_HAS_098C_CAMELLIA_CODES = 0; +static const int EVP_F_CAMELLIA_INIT_KEY = 0; +static const int EVP_R_CAMELLIA_KEY_SETUP_FAILED = 0; +#endif + +// OpenSSL without EC. e.g. RHEL +#ifndef OPENSSL_NO_EC +static const long Cryptography_HAS_EC_CODES = 1; +#else +static const long Cryptography_HAS_EC_CODES = 0; +static const int EC_R_UNKNOWN_GROUP = 0; +static const int EC_F_EC_GROUP_NEW_BY_CURVE_NAME = 0; +#endif + +#ifdef RSA_R_PKCS_DECODING_ERROR +static const long Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR = 1; +#else +static const long Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR = 0; +static const long RSA_R_PKCS_DECODING_ERROR = 0; +#endif +""" + +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", +    ], +    "Cryptography_HAS_098C_CAMELLIA_CODES": [ +        "EVP_F_CAMELLIA_INIT_KEY", +        "EVP_R_CAMELLIA_KEY_SETUP_FAILED" +    ], +    "Cryptography_HAS_EC_CODES": [ +        "EC_R_UNKNOWN_GROUP", +        "EC_F_EC_GROUP_NEW_BY_CURVE_NAME" +    ], +    "Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR": [ +        "RSA_R_PKCS_DECODING_ERROR" +    ] +} diff --git a/src/cryptography/hazmat/bindings/openssl/evp.py b/src/cryptography/hazmat/bindings/openssl/evp.py new file mode 100644 index 00000000..033b083b --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/evp.py @@ -0,0 +1,263 @@ +# 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 + +INCLUDES = """ +#include <openssl/evp.h> +""" + +TYPES = """ +typedef ... EVP_CIPHER; +typedef struct { +    const EVP_CIPHER *cipher; +    ENGINE *engine; +    int encrypt; +    ...; +} EVP_CIPHER_CTX; +typedef ... EVP_MD; +typedef struct env_md_ctx_st { +    ...; +} EVP_MD_CTX; + +typedef struct evp_pkey_st { +    int type; +    ...; +} EVP_PKEY; +typedef ... EVP_PKEY_CTX; +static const int EVP_PKEY_RSA; +static const int EVP_PKEY_DSA; +static const int EVP_PKEY_EC; +static const int EVP_MAX_MD_SIZE; +static const int EVP_CTRL_GCM_SET_IVLEN; +static const int EVP_CTRL_GCM_GET_TAG; +static const int EVP_CTRL_GCM_SET_TAG; + +static const int Cryptography_HAS_GCM; +static const int Cryptography_HAS_PBKDF2_HMAC; +static const int Cryptography_HAS_PKEY_CTX; +""" + +FUNCTIONS = """ +const EVP_CIPHER *EVP_get_cipherbyname(const char *); +int EVP_EncryptInit_ex(EVP_CIPHER_CTX *, const EVP_CIPHER *, ENGINE *, +                       const unsigned char *, const unsigned char *); +int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *, int); +int EVP_EncryptUpdate(EVP_CIPHER_CTX *, unsigned char *, int *, +                      const unsigned char *, int); +int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *, unsigned char *, int *); +int EVP_DecryptInit_ex(EVP_CIPHER_CTX *, const EVP_CIPHER *, ENGINE *, +                       const unsigned char *, const unsigned char *); +int EVP_DecryptUpdate(EVP_CIPHER_CTX *, unsigned char *, int *, +                      const unsigned char *, int); +int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *, unsigned char *, int *); +int EVP_CipherInit_ex(EVP_CIPHER_CTX *, const EVP_CIPHER *, ENGINE *, +                      const unsigned char *, const unsigned char *, int); +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 *); +void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *); +EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void); +void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *); +int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *, int); + +EVP_MD_CTX *EVP_MD_CTX_create(void); +int EVP_MD_CTX_copy_ex(EVP_MD_CTX *, const EVP_MD_CTX *); +int EVP_DigestInit_ex(EVP_MD_CTX *, const EVP_MD *, ENGINE *); +int EVP_DigestUpdate(EVP_MD_CTX *, const void *, size_t); +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 *); + +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 *); +DSA *EVP_PKEY_get1_DSA(EVP_PKEY *); +DH *EVP_PKEY_get1_DH(EVP_PKEY *); + +int EVP_SignInit(EVP_MD_CTX *, const EVP_MD *); +int EVP_SignUpdate(EVP_MD_CTX *, const void *, size_t); +int EVP_SignFinal(EVP_MD_CTX *, unsigned char *, unsigned int *, EVP_PKEY *); + +int EVP_VerifyInit(EVP_MD_CTX *, const EVP_MD *); +int EVP_VerifyUpdate(EVP_MD_CTX *, const void *, size_t); +int EVP_VerifyFinal(EVP_MD_CTX *, const unsigned char *, unsigned int, +                    EVP_PKEY *); + +const EVP_MD *EVP_md5(void); + +int PKCS5_PBKDF2_HMAC_SHA1(const char *, int, const unsigned char *, int, int, +                           int, unsigned char *); + +int EVP_PKEY_set1_RSA(EVP_PKEY *, struct rsa_st *); +int EVP_PKEY_set1_DSA(EVP_PKEY *, struct dsa_st *); +int EVP_PKEY_set1_DH(EVP_PKEY *, DH *); + +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); + +int EVP_PKEY_cmp(const EVP_PKEY *, const EVP_PKEY *); +""" + +MACROS = """ +void OpenSSL_add_all_algorithms(void); +int EVP_PKEY_assign_RSA(EVP_PKEY *, RSA *); +int EVP_PKEY_assign_DSA(EVP_PKEY *, DSA *); + +int EVP_PKEY_assign_EC_KEY(EVP_PKEY *, EC_KEY *); +EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *); +int EVP_PKEY_set1_EC_KEY(EVP_PKEY *, EC_KEY *); + +int EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *); +int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *, int, int, void *); + +int PKCS5_PBKDF2_HMAC(const char *, int, const unsigned char *, int, int, +                      const EVP_MD *, int, unsigned char *); + +int EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX *, const EVP_MD *); + +/* These aren't macros, but must be in this section because they're not +   available in 0.9.8. */ +EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *, ENGINE *); +EVP_PKEY_CTX *EVP_PKEY_CTX_new_id(int, ENGINE *); +EVP_PKEY_CTX *EVP_PKEY_CTX_dup(EVP_PKEY_CTX *); +void EVP_PKEY_CTX_free(EVP_PKEY_CTX *); +int EVP_PKEY_sign_init(EVP_PKEY_CTX *); +int EVP_PKEY_sign(EVP_PKEY_CTX *, unsigned char *, size_t *, +                  const 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); +int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *); +int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *); + +/* 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 *); + +/* Must be in macros because EVP_PKEY_CTX is undefined in 0.9.8 */ +int Cryptography_EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, +                                  size_t *outlen, const unsigned char *in, +                                  size_t inlen); +int Cryptography_EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, +                                  size_t *outlen, const unsigned char *in, +                                  size_t inlen); +""" + +CUSTOMIZATIONS = """ +#ifdef EVP_CTRL_GCM_SET_TAG +const long Cryptography_HAS_GCM = 1; +#else +const long Cryptography_HAS_GCM = 0; +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 >= 0x10000000L +const long Cryptography_HAS_PBKDF2_HMAC = 1; +const long Cryptography_HAS_PKEY_CTX = 1; + +/* OpenSSL 0.9.8 defines EVP_PKEY_encrypt and EVP_PKEY_decrypt functions, +   but they are a completely different signature from the ones in 1.0.0+. +   These wrapper functions allows us to safely declare them on any version and +   conditionally remove them on 0.9.8. */ +int Cryptography_EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, +                                  size_t *outlen, const unsigned char *in, +                                  size_t inlen) { +    return EVP_PKEY_encrypt(ctx, out, outlen, in, inlen); +} +int Cryptography_EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, +                                  size_t *outlen, const unsigned char *in, +                                  size_t inlen) { +    return EVP_PKEY_decrypt(ctx, out, outlen, in, inlen); +} +#else +const long Cryptography_HAS_PBKDF2_HMAC = 0; +int (*PKCS5_PBKDF2_HMAC)(const char *, int, const unsigned char *, int, int, +                         const EVP_MD *, int, unsigned char *) = NULL; +const long Cryptography_HAS_PKEY_CTX = 0; +typedef void EVP_PKEY_CTX; +int (*EVP_PKEY_CTX_set_signature_md)(EVP_PKEY_CTX *, const EVP_MD *) = NULL; +int (*EVP_PKEY_sign_init)(EVP_PKEY_CTX *) = NULL; +int (*EVP_PKEY_sign)(EVP_PKEY_CTX *, unsigned char *, size_t *, +                     const unsigned char *, size_t) = NULL; +int (*EVP_PKEY_verify_init)(EVP_PKEY_CTX *) = NULL; +int (*EVP_PKEY_verify)(EVP_PKEY_CTX *, const unsigned char *, size_t, +                       const unsigned char *, size_t) = NULL; +EVP_PKEY_CTX *(*EVP_PKEY_CTX_new)(EVP_PKEY *, ENGINE *) = NULL; +EVP_PKEY_CTX *(*EVP_PKEY_CTX_new_id)(int, ENGINE *) = NULL; +EVP_PKEY_CTX *(*EVP_PKEY_CTX_dup)(EVP_PKEY_CTX *) = NULL; +void (*EVP_PKEY_CTX_free)(EVP_PKEY_CTX *) = NULL; +int (*EVP_PKEY_encrypt_init)(EVP_PKEY_CTX *) = NULL; +int (*EVP_PKEY_decrypt_init)(EVP_PKEY_CTX *) = NULL; +int (*Cryptography_EVP_PKEY_encrypt)(EVP_PKEY_CTX *, unsigned char *, size_t *, +                                     const unsigned char *, size_t) = NULL; +int (*Cryptography_EVP_PKEY_decrypt)(EVP_PKEY_CTX *, unsigned char *, size_t *, +                                     const unsigned char *, size_t) = NULL; +#endif +#ifdef OPENSSL_NO_EC +int (*EVP_PKEY_assign_EC_KEY)(EVP_PKEY *, EC_KEY *) = NULL; +EC_KEY *(*EVP_PKEY_get1_EC_KEY)(EVP_PKEY *) = NULL; +int (*EVP_PKEY_set1_EC_KEY)(EVP_PKEY *, EC_KEY *) = NULL; +#endif + +""" + +CONDITIONAL_NAMES = { +    "Cryptography_HAS_GCM": [ +        "EVP_CTRL_GCM_GET_TAG", +        "EVP_CTRL_GCM_SET_TAG", +        "EVP_CTRL_GCM_SET_IVLEN", +    ], +    "Cryptography_HAS_PBKDF2_HMAC": [ +        "PKCS5_PBKDF2_HMAC" +    ], +    "Cryptography_HAS_PKEY_CTX": [ +        "EVP_PKEY_CTX_new", +        "EVP_PKEY_CTX_new_id", +        "EVP_PKEY_CTX_dup", +        "EVP_PKEY_CTX_free", +        "EVP_PKEY_sign", +        "EVP_PKEY_sign_init", +        "EVP_PKEY_verify", +        "EVP_PKEY_verify_init", +        "Cryptography_EVP_PKEY_encrypt", +        "EVP_PKEY_encrypt_init", +        "Cryptography_EVP_PKEY_decrypt", +        "EVP_PKEY_decrypt_init", +        "EVP_PKEY_CTX_set_signature_md", +    ], +    "Cryptography_HAS_EC": [ +        "EVP_PKEY_assign_EC_KEY", +        "EVP_PKEY_get1_EC_KEY", +        "EVP_PKEY_set1_EC_KEY", +    ] +} diff --git a/src/cryptography/hazmat/bindings/openssl/hmac.py b/src/cryptography/hazmat/bindings/openssl/hmac.py new file mode 100644 index 00000000..6a64b92c --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/hmac.py @@ -0,0 +1,94 @@ +# 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 + +INCLUDES = """ +#include <openssl/hmac.h> +""" + +TYPES = """ +typedef struct { ...; } HMAC_CTX; +""" + +FUNCTIONS = """ +void HMAC_CTX_init(HMAC_CTX *); +void HMAC_CTX_cleanup(HMAC_CTX *); + +int Cryptography_HMAC_Init_ex(HMAC_CTX *, const void *, int, const EVP_MD *, +                              ENGINE *); +int Cryptography_HMAC_Update(HMAC_CTX *, const unsigned char *, size_t); +int Cryptography_HMAC_Final(HMAC_CTX *, unsigned char *, unsigned int *); +int Cryptography_HMAC_CTX_copy(HMAC_CTX *, HMAC_CTX *); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +int Cryptography_HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int key_len, +                              const EVP_MD *md, ENGINE *impl) { +#if OPENSSL_VERSION_NUMBER >= 0x010000000 +    return HMAC_Init_ex(ctx, key, key_len, md, impl); +#else +    HMAC_Init_ex(ctx, key, key_len, md, impl); +    return 1; +#endif +} + +int Cryptography_HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, +                             size_t data_len) { +#if OPENSSL_VERSION_NUMBER >= 0x010000000 +    return HMAC_Update(ctx, data, data_len); +#else +    HMAC_Update(ctx, data, data_len); +    return 1; +#endif +} + +int Cryptography_HMAC_Final(HMAC_CTX *ctx, unsigned char *digest, +    unsigned int *outlen) { +#if OPENSSL_VERSION_NUMBER >= 0x010000000 +    return HMAC_Final(ctx, digest, outlen); +#else +    HMAC_Final(ctx, digest, outlen); +    return 1; +#endif +} + +int Cryptography_HMAC_CTX_copy(HMAC_CTX *dst_ctx, HMAC_CTX *src_ctx) { +#if OPENSSL_VERSION_NUMBER >= 0x010000000 +    return HMAC_CTX_copy(dst_ctx, src_ctx); +#else +    HMAC_CTX_init(dst_ctx); +    if (!EVP_MD_CTX_copy_ex(&dst_ctx->i_ctx, &src_ctx->i_ctx)) { +        goto err; +    } +    if (!EVP_MD_CTX_copy_ex(&dst_ctx->o_ctx, &src_ctx->o_ctx)) { +        goto err; +    } +    if (!EVP_MD_CTX_copy_ex(&dst_ctx->md_ctx, &src_ctx->md_ctx)) { +        goto err; +    } +    memcpy(dst_ctx->key, src_ctx->key, HMAC_MAX_MD_CBLOCK); +    dst_ctx->key_length = src_ctx->key_length; +    dst_ctx->md = src_ctx->md; +    return 1; + +    err: +        return 0; +#endif +} +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/nid.py b/src/cryptography/hazmat/bindings/openssl/nid.py new file mode 100644 index 00000000..b2e61492 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/nid.py @@ -0,0 +1,241 @@ +# 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 + +INCLUDES = """ +#include <openssl/obj_mac.h> +""" + +TYPES = """ +static const int Cryptography_HAS_ECDSA_SHA2_NIDS; + +static const int NID_undef; +static const int NID_dsa; +static const int NID_dsaWithSHA; +static const int NID_dsaWithSHA1; +static const int NID_md2; +static const int NID_md4; +static const int NID_md5; +static const int NID_mdc2; +static const int NID_ripemd160; +static const int NID_sha; +static const int NID_sha1; +static const int NID_sha256; +static const int NID_sha384; +static const int NID_sha512; +static const int NID_sha224; +static const int NID_sha; +static const int NID_ecdsa_with_SHA1; +static const int NID_ecdsa_with_SHA224; +static const int NID_ecdsa_with_SHA256; +static const int NID_ecdsa_with_SHA384; +static const int NID_ecdsa_with_SHA512; +static const int NID_pbe_WithSHA1And3_Key_TripleDES_CBC; +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; + +static const int NID_subject_key_identifier; +static const int NID_authority_key_identifier; +static const int NID_policy_constraints; +static const int NID_ext_key_usage; +static const int NID_info_access; +static const int NID_key_usage; +static const int NID_subject_alt_name; +static const int NID_issuer_alt_name; +static const int NID_basic_constraints; +static const int NID_issuing_distribution_point; +static const int NID_certificate_issuer; +static const int NID_name_constraints; +static const int NID_crl_distribution_points; +static const int NID_certificate_policies; +static const int NID_inhibit_any_policy; + +static const int NID_private_key_usage_period; +static const int NID_crl_number; +static const int NID_crl_reason; +static const int NID_invalidity_date; +static const int NID_delta_crl; +static const int NID_any_policy; +static const int NID_policy_mappings; +static const int NID_target_information; +static const int NID_no_rev_avail; +""" + +FUNCTIONS = """ +""" + +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 = { +    "Cryptography_HAS_ECDSA_SHA2_NIDS": [ +        "NID_ecdsa_with_SHA224", +        "NID_ecdsa_with_SHA256", +        "NID_ecdsa_with_SHA384", +        "NID_ecdsa_with_SHA512", +    ], +} diff --git a/src/cryptography/hazmat/bindings/openssl/objects.py b/src/cryptography/hazmat/bindings/openssl/objects.py new file mode 100644 index 00000000..557c0158 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/objects.py @@ -0,0 +1,45 @@ +# 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 + +INCLUDES = """ +#include <openssl/objects.h> +""" + +TYPES = """ +""" + +FUNCTIONS = """ +ASN1_OBJECT *OBJ_nid2obj(int); +const char *OBJ_nid2ln(int); +const char *OBJ_nid2sn(int); +int OBJ_obj2nid(const ASN1_OBJECT *); +int OBJ_ln2nid(const char *); +int OBJ_sn2nid(const char *); +int OBJ_txt2nid(const char *); +ASN1_OBJECT *OBJ_txt2obj(const char *, int); +int OBJ_obj2txt(char *, int, const ASN1_OBJECT *, int); +int OBJ_cmp(const ASN1_OBJECT *, const ASN1_OBJECT *); +ASN1_OBJECT *OBJ_dup(const ASN1_OBJECT *); +int OBJ_create(const char *, const char *, const char *); +void OBJ_cleanup(void); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/opensslv.py b/src/cryptography/hazmat/bindings/openssl/opensslv.py new file mode 100644 index 00000000..ef6e057b --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/opensslv.py @@ -0,0 +1,36 @@ +# 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 + +INCLUDES = """ +#include <openssl/opensslv.h> +""" + +TYPES = """ +/* Note that these will be resolved when cryptography is compiled and are NOT +   guaranteed to be the version that it actually loads. */ +static const int OPENSSL_VERSION_NUMBER; +static const char *const OPENSSL_VERSION_TEXT; +""" + +FUNCTIONS = """ +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/osrandom_engine.py b/src/cryptography/hazmat/bindings/openssl/osrandom_engine.py new file mode 100644 index 00000000..462997cc --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/osrandom_engine.py @@ -0,0 +1,218 @@ +# 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 + +INCLUDES = """ +#ifdef _WIN32 +#include <Wincrypt.h> +#else +#include <fcntl.h> +#include <unistd.h> +#endif +""" + +TYPES = """ +static const char *const Cryptography_osrandom_engine_name; +static const char *const Cryptography_osrandom_engine_id; +""" + +FUNCTIONS = """ +int Cryptography_add_osrandom_engine(void); +""" + +MACROS = """ +""" + +WIN32_CUSTOMIZATIONS = """ +static HCRYPTPROV hCryptProv = 0; + +static int osrandom_init(ENGINE *e) { +    if (hCryptProv > 0) { +        return 1; +    } +    if (CryptAcquireContext(&hCryptProv, NULL, NULL, +                            PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { +        return 1; +    } else { +        return 0; +    } +} + +static int osrandom_rand_bytes(unsigned char *buffer, int size) { +    if (hCryptProv == 0) { +        return 0; +    } + +    if (!CryptGenRandom(hCryptProv, (DWORD)size, buffer)) { +        ERR_put_error( +            ERR_LIB_RAND, 0, ERR_R_RAND_LIB, "osrandom_engine.py", 0 +        ); +        return 0; +    } +    return 1; +} + +static int osrandom_finish(ENGINE *e) { +    if (CryptReleaseContext(hCryptProv, 0)) { +        hCryptProv = 0; +        return 1; +    } else { +        return 0; +    } +} + +static int osrandom_rand_status(void) { +    if (hCryptProv == 0) { +        return 0; +    } else { +        return 1; +    } +} +""" + +POSIX_CUSTOMIZATIONS = """ +static int urandom_fd = -1; + +static int osrandom_finish(ENGINE *e); + +static int osrandom_init(ENGINE *e) { +    if (urandom_fd > -1) { +        return 1; +    } +    urandom_fd = open("/dev/urandom", O_RDONLY); +    if (urandom_fd > -1) { +        int flags = fcntl(urandom_fd, F_GETFD); +        if (flags == -1) { +            osrandom_finish(e); +            return 0; +        } else if (fcntl(urandom_fd, F_SETFD, flags | FD_CLOEXEC) == -1) { +            osrandom_finish(e); +            return 0; +        } +        return 1; +    } else { +        return 0; +    } +} + +static int osrandom_rand_bytes(unsigned char *buffer, int size) { +    ssize_t n; +    while (size > 0) { +        do { +            n = read(urandom_fd, buffer, (size_t)size); +        } while (n < 0 && errno == EINTR); +        if (n <= 0) { +            ERR_put_error( +                ERR_LIB_RAND, 0, ERR_R_RAND_LIB, "osrandom_engine.py", 0 +            ); +            return 0; +        } +        buffer += n; +        size -= n; +    } +    return 1; +} + +static int osrandom_finish(ENGINE *e) { +    int n; +    do { +        n = close(urandom_fd); +    } while (n < 0 && errno == EINTR); +    urandom_fd = -1; +    if (n < 0) { +        return 0; +    } else { +        return 1; +    } +} + +static int osrandom_rand_status(void) { +    if (urandom_fd == -1) { +        return 0; +    } else { +        return 1; +    } +} +""" + +CUSTOMIZATIONS = """ +static const char *Cryptography_osrandom_engine_id = "osrandom"; +static const char *Cryptography_osrandom_engine_name = "osrandom_engine"; + +#if defined(_WIN32) +%(WIN32_CUSTOMIZATIONS)s +#else +%(POSIX_CUSTOMIZATIONS)s +#endif + +/* This replicates the behavior of the OpenSSL FIPS RNG, which returns a +   -1 in the event that there is an error when calling RAND_pseudo_bytes. */ +static int osrandom_pseudo_rand_bytes(unsigned char *buffer, int size) { +    int res = osrandom_rand_bytes(buffer, size); +    if (res == 0) { +        return -1; +    } else { +        return res; +    } +} + +static RAND_METHOD osrandom_rand = { +    NULL, +    osrandom_rand_bytes, +    NULL, +    NULL, +    osrandom_pseudo_rand_bytes, +    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; +    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; +    } +    if(!ENGINE_set_id(e, Cryptography_osrandom_engine_id) || +            !ENGINE_set_name(e, Cryptography_osrandom_engine_name) || +            !ENGINE_set_RAND(e, &osrandom_rand) || +            !ENGINE_set_init_function(e, osrandom_init) || +            !ENGINE_set_finish_function(e, osrandom_finish)) { +        ENGINE_free(e); +        return 0; +    } +    if (!ENGINE_add(e)) { +        ENGINE_free(e); +        return 0; +    } +    if (!ENGINE_free(e)) { +        return 0; +    } + +    return 1; +} +""" % { +    "WIN32_CUSTOMIZATIONS": WIN32_CUSTOMIZATIONS, +    "POSIX_CUSTOMIZATIONS": POSIX_CUSTOMIZATIONS, +} + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/pem.py b/src/cryptography/hazmat/bindings/openssl/pem.py new file mode 100644 index 00000000..752f1987 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/pem.py @@ -0,0 +1,89 @@ +# 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 + +INCLUDES = """ +#include <openssl/pem.h> +""" + +TYPES = """ +typedef int pem_password_cb(char *buf, int size, int rwflag, void *userdata); +""" + +FUNCTIONS = """ +X509 *PEM_read_bio_X509(BIO *, X509 **, pem_password_cb *, void *); +int PEM_write_bio_X509(BIO *, X509 *); + +int PEM_write_bio_PrivateKey(BIO *, EVP_PKEY *, const EVP_CIPHER *, +                             unsigned char *, int, pem_password_cb *, void *); + +EVP_PKEY *PEM_read_bio_PrivateKey(BIO *, EVP_PKEY **, pem_password_cb *, +                                 void *); + +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 *); + +PKCS7 *d2i_PKCS7_bio(BIO *, PKCS7 **); +EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *, EVP_PKEY **, pem_password_cb *, +                                  void *); + +int PEM_write_bio_X509_REQ(BIO *, X509_REQ *); + +X509_REQ *PEM_read_bio_X509_REQ(BIO *, X509_REQ **, pem_password_cb *, void *); + +X509_CRL *PEM_read_bio_X509_CRL(BIO *, X509_CRL **, pem_password_cb *, void *); + +int PEM_write_bio_X509_CRL(BIO *, X509_CRL *); + +PKCS7 *PEM_read_bio_PKCS7(BIO *, PKCS7 **, pem_password_cb *, void *); +DH *PEM_read_bio_DHparams(BIO *, DH **, pem_password_cb *, void *); + +DSA *PEM_read_bio_DSAPrivateKey(BIO *, DSA **, pem_password_cb *, void *); + +RSA *PEM_read_bio_RSAPrivateKey(BIO *, RSA **, pem_password_cb *, void *); + +int PEM_write_bio_DSAPrivateKey(BIO *, DSA *, const EVP_CIPHER *, +                                unsigned char *, int, +                                pem_password_cb *, void *); + +int PEM_write_bio_RSAPrivateKey(BIO *, RSA *, const EVP_CIPHER *, +                                unsigned char *, int, +                                pem_password_cb *, void *); + +DSA *PEM_read_bio_DSA_PUBKEY(BIO *, DSA **, pem_password_cb *, void *); + +RSA *PEM_read_bio_RSAPublicKey(BIO *, RSA **, pem_password_cb *, void *); + +int PEM_write_bio_DSA_PUBKEY(BIO *, DSA *); + +int PEM_write_bio_RSAPublicKey(BIO *, const RSA *); + +EVP_PKEY *PEM_read_bio_PUBKEY(BIO *, EVP_PKEY **, pem_password_cb *, void *); +int PEM_write_bio_PUBKEY(BIO *, EVP_PKEY *); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/pkcs12.py b/src/cryptography/hazmat/bindings/openssl/pkcs12.py new file mode 100644 index 00000000..a8f106f6 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/pkcs12.py @@ -0,0 +1,41 @@ +# 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 + +INCLUDES = """ +#include <openssl/pkcs12.h> +""" + +TYPES = """ +typedef ... PKCS12; +""" + +FUNCTIONS = """ +void PKCS12_free(PKCS12 *); + +PKCS12 *d2i_PKCS12_bio(BIO *, PKCS12 **); +int i2d_PKCS12_bio(BIO *, PKCS12 *); +""" + +MACROS = """ +int PKCS12_parse(PKCS12 *, const char *, EVP_PKEY **, X509 **, +                 Cryptography_STACK_OF_X509 **); +PKCS12 *PKCS12_create(char *, char *, EVP_PKEY *, X509 *, +                      Cryptography_STACK_OF_X509 *, int, int, int, int, int); +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/pkcs7.py b/src/cryptography/hazmat/bindings/openssl/pkcs7.py new file mode 100644 index 00000000..1343e566 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/pkcs7.py @@ -0,0 +1,41 @@ +# 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 + +INCLUDES = """ +#include <openssl/pkcs7.h> +""" + +TYPES = """ +typedef struct { +    ASN1_OBJECT *type; +    ...; +} PKCS7; +""" + +FUNCTIONS = """ +void PKCS7_free(PKCS7 *); +""" + +MACROS = """ +int PKCS7_type_is_signed(PKCS7 *); +int PKCS7_type_is_enveloped(PKCS7 *); +int PKCS7_type_is_signedAndEnveloped(PKCS7 *); +int PKCS7_type_is_data(PKCS7 *); +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/rand.py b/src/cryptography/hazmat/bindings/openssl/rand.py new file mode 100644 index 00000000..7b1be9df --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/rand.py @@ -0,0 +1,45 @@ +# 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 + +INCLUDES = """ +#include <openssl/rand.h> +""" + +TYPES = """ +""" + +FUNCTIONS = """ +void ERR_load_RAND_strings(void); +void RAND_seed(const void *, int); +void RAND_add(const void *, int, double); +int RAND_status(void); +int RAND_egd(const char *); +int RAND_egd_bytes(const char *, int); +int RAND_query_egd_bytes(const char *, unsigned char *, int); +const char *RAND_file_name(char *, size_t); +int RAND_load_file(const char *, long); +int RAND_write_file(const char *); +void RAND_cleanup(void); +int RAND_bytes(unsigned char *, int); +int RAND_pseudo_bytes(unsigned char *, int); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/rsa.py b/src/cryptography/hazmat/bindings/openssl/rsa.py new file mode 100644 index 00000000..cb8e701e --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/rsa.py @@ -0,0 +1,108 @@ +# 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 + +INCLUDES = """ +#include <openssl/rsa.h> +""" + +TYPES = """ +typedef struct rsa_st { +    BIGNUM *n; +    BIGNUM *e; +    BIGNUM *d; +    BIGNUM *p; +    BIGNUM *q; +    BIGNUM *dmp1; +    BIGNUM *dmq1; +    BIGNUM *iqmp; +    ...; +} RSA; +typedef ... BN_GENCB; +static const int RSA_PKCS1_PADDING; +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 = """ +RSA *RSA_new(void); +void RSA_free(RSA *); +int RSA_size(const RSA *); +int RSA_generate_key_ex(RSA *, int, BIGNUM *, BN_GENCB *); +int RSA_check_key(const RSA *); +RSA *RSAPublicKey_dup(RSA *); +int RSA_blinding_on(RSA *, BN_CTX *); +void RSA_blinding_off(RSA *); +int RSA_public_encrypt(int, const unsigned char *, unsigned char *, +                       RSA *, int); +int RSA_private_encrypt(int, const unsigned char *, unsigned char *, +                        RSA *, int); +int RSA_public_decrypt(int, const unsigned char *, unsigned char *, +                       RSA *, int); +int RSA_private_decrypt(int, const unsigned char *, unsigned char *, +                        RSA *, int); +int RSA_print(BIO *, const RSA *, int); +int RSA_verify_PKCS1_PSS(RSA *, const unsigned char *, const EVP_MD *, +                         const unsigned char *, int); +int RSA_padding_add_PKCS1_PSS(RSA *, unsigned char *, const unsigned char *, +                              const EVP_MD *, int); +int RSA_padding_add_PKCS1_OAEP(unsigned char *, int, const unsigned char *, +                               int, const unsigned char *, int); +int RSA_padding_check_PKCS1_OAEP(unsigned char *, int, const unsigned char *, +                                 int, int, const unsigned char *, int); +""" + +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 +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 +""" + +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/src/cryptography/hazmat/bindings/openssl/ssl.py b/src/cryptography/hazmat/bindings/openssl/ssl.py new file mode 100644 index 00000000..7d805e78 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/ssl.py @@ -0,0 +1,620 @@ +# 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 + +INCLUDES = """ +#include <openssl/ssl.h> + +typedef STACK_OF(SSL_CIPHER) Cryptography_STACK_OF_SSL_CIPHER; +""" + +TYPES = """ +/* + * Internally invented symbols to tell which versions of SSL/TLS are supported. +*/ +static const long Cryptography_HAS_SSL2; +static const long Cryptography_HAS_TLSv1_1; +static const long Cryptography_HAS_TLSv1_2; +static const long Cryptography_HAS_SECURE_RENEGOTIATION; + +/* Internally invented symbol to tell us if SNI is supported */ +static const long Cryptography_HAS_TLSEXT_HOSTNAME; + +/* Internally invented symbol to tell us if SSL_MODE_RELEASE_BUFFERS is + * supported + */ +static const long Cryptography_HAS_RELEASE_BUFFERS; + +/* Internally invented symbol to tell us if SSL_OP_NO_COMPRESSION is + * supported + */ +static const long Cryptography_HAS_OP_NO_COMPRESSION; + +static const long Cryptography_HAS_SSL_OP_MSIE_SSLV2_RSA_PADDING; +static const long Cryptography_HAS_SSL_SET_SSL_CTX; +static const long Cryptography_HAS_SSL_OP_NO_TICKET; +static const long Cryptography_HAS_NETBSD_D1_METH; +static const long Cryptography_HAS_NEXTPROTONEG; +static const long Cryptography_HAS_ALPN; + +static const long SSL_FILETYPE_PEM; +static const long SSL_FILETYPE_ASN1; +static const long SSL_ERROR_NONE; +static const long SSL_ERROR_ZERO_RETURN; +static const long SSL_ERROR_WANT_READ; +static const long SSL_ERROR_WANT_WRITE; +static const long SSL_ERROR_WANT_X509_LOOKUP; +static const long SSL_ERROR_SYSCALL; +static const long SSL_ERROR_SSL; +static const long SSL_SENT_SHUTDOWN; +static const long SSL_RECEIVED_SHUTDOWN; +static const long SSL_OP_NO_SSLv2; +static const long SSL_OP_NO_SSLv3; +static const long SSL_OP_NO_TLSv1; +static const long SSL_OP_NO_TLSv1_1; +static const long SSL_OP_NO_TLSv1_2; +static const long SSL_OP_NO_COMPRESSION; +static const long SSL_OP_SINGLE_DH_USE; +static const long SSL_OP_EPHEMERAL_RSA; +static const long SSL_OP_MICROSOFT_SESS_ID_BUG; +static const long SSL_OP_NETSCAPE_CHALLENGE_BUG; +static const long SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG; +static const long SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG; +static const long SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER; +static const long SSL_OP_MSIE_SSLV2_RSA_PADDING; +static const long SSL_OP_SSLEAY_080_CLIENT_DH_BUG; +static const long SSL_OP_TLS_D5_BUG; +static const long SSL_OP_TLS_BLOCK_PADDING_BUG; +static const long SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; +static const long SSL_OP_CIPHER_SERVER_PREFERENCE; +static const long SSL_OP_TLS_ROLLBACK_BUG; +static const long SSL_OP_PKCS1_CHECK_1; +static const long SSL_OP_PKCS1_CHECK_2; +static const long SSL_OP_NETSCAPE_CA_DN_BUG; +static const long SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG; +static const long SSL_OP_NO_QUERY_MTU; +static const long SSL_OP_COOKIE_EXCHANGE; +static const long SSL_OP_NO_TICKET; +static const long SSL_OP_ALL; +static const long SSL_OP_SINGLE_ECDH_USE; +static const long SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; +static const long SSL_OP_LEGACY_SERVER_CONNECT; +static const long SSL_VERIFY_PEER; +static const long SSL_VERIFY_FAIL_IF_NO_PEER_CERT; +static const long SSL_VERIFY_CLIENT_ONCE; +static const long SSL_VERIFY_NONE; +static const long SSL_SESS_CACHE_OFF; +static const long SSL_SESS_CACHE_CLIENT; +static const long SSL_SESS_CACHE_SERVER; +static const long SSL_SESS_CACHE_BOTH; +static const long SSL_SESS_CACHE_NO_AUTO_CLEAR; +static const long SSL_SESS_CACHE_NO_INTERNAL_LOOKUP; +static const long SSL_SESS_CACHE_NO_INTERNAL_STORE; +static const long SSL_SESS_CACHE_NO_INTERNAL; +static const long SSL_ST_CONNECT; +static const long SSL_ST_ACCEPT; +static const long SSL_ST_MASK; +static const long SSL_ST_INIT; +static const long SSL_ST_BEFORE; +static const long SSL_ST_OK; +static const long SSL_ST_RENEGOTIATE; +static const long SSL_CB_LOOP; +static const long SSL_CB_EXIT; +static const long SSL_CB_READ; +static const long SSL_CB_WRITE; +static const long SSL_CB_ALERT; +static const long SSL_CB_READ_ALERT; +static const long SSL_CB_WRITE_ALERT; +static const long SSL_CB_ACCEPT_LOOP; +static const long SSL_CB_ACCEPT_EXIT; +static const long SSL_CB_CONNECT_LOOP; +static const long SSL_CB_CONNECT_EXIT; +static const long SSL_CB_HANDSHAKE_START; +static const long SSL_CB_HANDSHAKE_DONE; +static const long SSL_MODE_RELEASE_BUFFERS; +static const long SSL_MODE_ENABLE_PARTIAL_WRITE; +static const long SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER; +static const long SSL_MODE_AUTO_RETRY; +static const long SSL3_RANDOM_SIZE; +typedef ... SSL_METHOD; +typedef struct ssl_st { +    int version; +    int type; +    ...; +} SSL_CTX; + +typedef struct { +    int master_key_length; +    unsigned char master_key[...]; +    ...; +} SSL_SESSION; + +typedef struct { +    unsigned char server_random[...]; +    unsigned char client_random[...]; +    ...; +} SSL3_STATE; + +typedef struct { +    SSL3_STATE *s3; +    SSL_SESSION *session; +    int type; +    ...; +} SSL; + +static const long TLSEXT_NAMETYPE_host_name; + +typedef ... SSL_CIPHER; +typedef ... Cryptography_STACK_OF_SSL_CIPHER; +typedef ... COMP_METHOD; +""" + +FUNCTIONS = """ +void SSL_load_error_strings(void); +int SSL_library_init(void); + +/*  SSL */ +const char *SSL_state_string_long(const SSL *); +SSL_SESSION *SSL_get1_session(SSL *); +int SSL_set_session(SSL *, SSL_SESSION *); +int SSL_get_verify_mode(const SSL *); +void SSL_set_verify_depth(SSL *, int); +int SSL_get_verify_depth(const SSL *); +int (*SSL_get_verify_callback(const SSL *))(int, X509_STORE_CTX *); +void SSL_set_info_callback(SSL *ssl, void (*)(const SSL *, int, int)); +void (*SSL_get_info_callback(const SSL *))(const SSL *, int, int); +SSL *SSL_new(SSL_CTX *); +void SSL_free(SSL *); +int SSL_set_fd(SSL *, int); +void SSL_set_bio(SSL *, BIO *, BIO *); +void SSL_set_connect_state(SSL *); +void SSL_set_accept_state(SSL *); +void SSL_set_shutdown(SSL *, int); +int SSL_get_shutdown(const SSL *); +int SSL_pending(const SSL *); +int SSL_write(SSL *, const void *, int); +int SSL_read(SSL *, void *, int); +X509 *SSL_get_peer_certificate(const SSL *); +int SSL_get_ex_data_X509_STORE_CTX_idx(void); + +Cryptography_STACK_OF_X509 *SSL_get_peer_cert_chain(const SSL *); +Cryptography_STACK_OF_X509_NAME *SSL_get_client_CA_list(const SSL *); + +int SSL_get_error(const SSL *, int); +int SSL_do_handshake(SSL *); +int SSL_shutdown(SSL *); +const char *SSL_get_cipher_list(const SSL *, int); +Cryptography_STACK_OF_SSL_CIPHER *SSL_get_ciphers(const SSL *); + +const COMP_METHOD *SSL_get_current_compression(SSL *); +const COMP_METHOD *SSL_get_current_expansion(SSL *); +const char *SSL_COMP_get_name(const COMP_METHOD *); + +/*  context */ +void SSL_CTX_free(SSL_CTX *); +long SSL_CTX_set_timeout(SSL_CTX *, long); +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 *); +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 *); +int SSL_CTX_load_verify_locations(SSL_CTX *, const char *, const char *); +void SSL_CTX_set_default_passwd_cb(SSL_CTX *, pem_password_cb *); +void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *, void *); +int SSL_CTX_use_certificate(SSL_CTX *, X509 *); +int SSL_CTX_use_certificate_file(SSL_CTX *, const char *, int); +int SSL_CTX_use_certificate_chain_file(SSL_CTX *, const char *); +int SSL_CTX_use_PrivateKey(SSL_CTX *, EVP_PKEY *); +int SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int); +void SSL_CTX_set_cert_store(SSL_CTX *, X509_STORE *); +X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *); +int SSL_CTX_add_client_CA(SSL_CTX *, X509 *); + +void SSL_CTX_set_client_CA_list(SSL_CTX *, Cryptography_STACK_OF_X509_NAME *); + +/*  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 = """ +unsigned long SSL_set_mode(SSL *, unsigned long); +unsigned long SSL_get_mode(SSL *); + +unsigned long SSL_set_options(SSL *, unsigned long); +unsigned long SSL_get_options(SSL *); + +int SSL_want_read(const SSL *); +int SSL_want_write(const SSL *); + +long SSL_total_renegotiations(SSL *); +long SSL_get_secure_renegotiation_support(SSL *); + +/* Defined as unsigned long because SSL_OP_ALL is greater than signed 32-bit +   and Windows defines long as 32-bit. */ +unsigned long SSL_CTX_set_options(SSL_CTX *, unsigned long); +unsigned long SSL_CTX_get_options(SSL_CTX *); +unsigned long SSL_CTX_set_mode(SSL_CTX *, unsigned long); +unsigned long SSL_CTX_get_mode(SSL_CTX *); +unsigned long SSL_CTX_set_session_cache_mode(SSL_CTX *, unsigned long); +unsigned long SSL_CTX_get_session_cache_mode(SSL_CTX *); +unsigned long SSL_CTX_set_tmp_dh(SSL_CTX *, DH *); +unsigned long SSL_CTX_set_tmp_ecdh(SSL_CTX *, EC_KEY *); +unsigned long SSL_CTX_add_extra_chain_cert(SSL_CTX *, X509 *); + +/*- These aren't macros these functions are all const X on openssl > 1.0.x -*/ + +/*  methods */ + +/* SSLv2 support is compiled out of some versions of OpenSSL.  These will + * get special support when we generate the bindings so that if they are + * available they will be wrapped, but if they are not they won't cause + * problems (like link errors). + */ +const SSL_METHOD *SSLv2_method(void); +const SSL_METHOD *SSLv2_server_method(void); +const SSL_METHOD *SSLv2_client_method(void); + +/* + * TLSv1_1 and TLSv1_2 are recent additions.  Only sufficiently new versions of + * OpenSSL support them. + */ +const SSL_METHOD *TLSv1_1_method(void); +const SSL_METHOD *TLSv1_1_server_method(void); +const SSL_METHOD *TLSv1_1_client_method(void); + +const SSL_METHOD *TLSv1_2_method(void); +const SSL_METHOD *TLSv1_2_server_method(void); +const SSL_METHOD *TLSv1_2_client_method(void); + +const SSL_METHOD *SSLv3_method(void); +const SSL_METHOD *SSLv3_server_method(void); +const SSL_METHOD *SSLv3_client_method(void); + +const SSL_METHOD *TLSv1_method(void); +const SSL_METHOD *TLSv1_server_method(void); +const SSL_METHOD *TLSv1_client_method(void); + +const SSL_METHOD *DTLSv1_method(void); +const SSL_METHOD *DTLSv1_server_method(void); +const SSL_METHOD *DTLSv1_client_method(void); + +const SSL_METHOD *SSLv23_method(void); +const SSL_METHOD *SSLv23_server_method(void); +const SSL_METHOD *SSLv23_client_method(void); + +/*- These aren't macros these arguments are all const X on openssl > 1.0.x -*/ +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. + */ +const char *SSL_get_servername(const SSL *, const int); +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 *); + +const SSL_METHOD* Cryptography_SSL_CTX_get_method(const SSL_CTX*); + +/* NPN APIs were introduced in OpenSSL 1.0.1.  To continue to support earlier + * versions some special handling of these is necessary. + */ +void SSL_CTX_set_next_protos_advertised_cb(SSL_CTX *, +                                           int (*)(SSL *, +                                                   const unsigned char **, +                                                   unsigned int *, +                                                   void *), +                                           void *); +void SSL_CTX_set_next_proto_select_cb(SSL_CTX *, +                                      int (*)(SSL *, +                                              unsigned char **, +                                              unsigned char *, +                                              const unsigned char *, +                                              unsigned int, +                                              void *), +                                      void *); +int SSL_select_next_proto(unsigned char **, unsigned char *, +                          const unsigned char *, unsigned int, +                          const unsigned char *, unsigned int); +void SSL_get0_next_proto_negotiated(const SSL *, +                                    const unsigned char **, unsigned *); + +int sk_SSL_CIPHER_num(Cryptography_STACK_OF_SSL_CIPHER *); +SSL_CIPHER *sk_SSL_CIPHER_value(Cryptography_STACK_OF_SSL_CIPHER *, int); + +/* ALPN APIs were introduced in OpenSSL 1.0.2.  To continue to support earlier + * versions some special handling of these is necessary. + */ +int SSL_CTX_set_alpn_protos(SSL_CTX *, const unsigned char*, unsigned); +int SSL_set_alpn_protos(SSL *, const unsigned char*, unsigned); +void SSL_CTX_set_alpn_select_cb(SSL_CTX *, +                                int (*) (SSL *, +                                         const unsigned char **, +                                         unsigned char *, +                                         const unsigned char *, +                                         unsigned int, +                                         void *), +                                void *); +void SSL_get0_alpn_selected(const SSL *, const unsigned char **, unsigned *); +""" + +CUSTOMIZATIONS = """ +/** Secure renegotiation is supported in OpenSSL >= 0.9.8m + *  But some Linux distributions have back ported some features. + */ +#ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION +static const long Cryptography_HAS_SECURE_RENEGOTIATION = 0; +long (*SSL_get_secure_renegotiation_support)(SSL *) = NULL; +const long SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 0; +const long SSL_OP_LEGACY_SERVER_CONNECT = 0; +#else +static const long Cryptography_HAS_SECURE_RENEGOTIATION = 1; +#endif +#ifdef OPENSSL_NO_SSL2 +static const long Cryptography_HAS_SSL2 = 0; +SSL_METHOD* (*SSLv2_method)(void) = NULL; +SSL_METHOD* (*SSLv2_client_method)(void) = NULL; +SSL_METHOD* (*SSLv2_server_method)(void) = NULL; +#else +static const long Cryptography_HAS_SSL2 = 1; +#endif + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME +static const long Cryptography_HAS_TLSEXT_HOSTNAME = 1; +#else +static const long Cryptography_HAS_TLSEXT_HOSTNAME = 0; +void (*SSL_set_tlsext_host_name)(SSL *, char *) = NULL; +const char* (*SSL_get_servername)(const SSL *, const int) = NULL; +void (*SSL_CTX_set_tlsext_servername_callback)( +    SSL_CTX *, +    int (*)(const SSL *, int *, void *)) = NULL; +#endif + +#ifdef SSL_MODE_RELEASE_BUFFERS +static const long Cryptography_HAS_RELEASE_BUFFERS = 1; +#else +static const long Cryptography_HAS_RELEASE_BUFFERS = 0; +const long SSL_MODE_RELEASE_BUFFERS = 0; +#endif + +#ifdef SSL_OP_NO_COMPRESSION +static const long Cryptography_HAS_OP_NO_COMPRESSION = 1; +#else +static const long Cryptography_HAS_OP_NO_COMPRESSION = 0; +const long SSL_OP_NO_COMPRESSION = 0; +#endif + +#ifdef SSL_OP_NO_TLSv1_1 +static const long Cryptography_HAS_TLSv1_1 = 1; +#else +static const long Cryptography_HAS_TLSv1_1 = 0; +static const long SSL_OP_NO_TLSv1_1 = 0; +SSL_METHOD* (*TLSv1_1_method)(void) = NULL; +SSL_METHOD* (*TLSv1_1_client_method)(void) = NULL; +SSL_METHOD* (*TLSv1_1_server_method)(void) = NULL; +#endif + +#ifdef SSL_OP_NO_TLSv1_2 +static const long Cryptography_HAS_TLSv1_2 = 1; +#else +static const long Cryptography_HAS_TLSv1_2 = 0; +static const long SSL_OP_NO_TLSv1_2 = 0; +SSL_METHOD* (*TLSv1_2_method)(void) = NULL; +SSL_METHOD* (*TLSv1_2_client_method)(void) = NULL; +SSL_METHOD* (*TLSv1_2_server_method)(void) = NULL; +#endif + +#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING +static const long Cryptography_HAS_SSL_OP_MSIE_SSLV2_RSA_PADDING = 1; +#else +static const long Cryptography_HAS_SSL_OP_MSIE_SSLV2_RSA_PADDING = 0; +const long SSL_OP_MSIE_SSLV2_RSA_PADDING = 0; +#endif + +#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 long TLSEXT_NAMETYPE_host_name = 0; +SSL_CTX *(*SSL_set_SSL_CTX)(SSL *, SSL_CTX *) = NULL; +#endif + +/* NetBSD shipped without including d1_meth.c. This workaround checks to see +   if the version of NetBSD we're currently running on is old enough to +   have the bug and provides an empty implementation so we can link and +   then remove the function from the ffi object. */ +#ifdef __NetBSD__ +#  include <sys/param.h> +#  if (__NetBSD_Version__ < 699003800) +static const long Cryptography_HAS_NETBSD_D1_METH = 0; +const SSL_METHOD *DTLSv1_method(void) { +    return NULL; +} +#  else +static const long Cryptography_HAS_NETBSD_D1_METH = 1; +#  endif +#else +static const long Cryptography_HAS_NETBSD_D1_METH = 1; +#endif + +/* Workaround for #794 caused by cffi const** bug. */ +const SSL_METHOD* Cryptography_SSL_CTX_get_method(const SSL_CTX* ctx) { +    return ctx->method; +} + +/* Because OPENSSL defines macros that claim lack of support for things, rather + * than macros that claim support for things, we need to do a version check in + * addition to a definition check. NPN was added in 1.0.1: for any version + * before that, there is no compatibility. + */ +#if defined(OPENSSL_NO_NEXTPROTONEG) || OPENSSL_VERSION_NUMBER < 0x1000100fL +static const long Cryptography_HAS_NEXTPROTONEG = 0; +void (*SSL_CTX_set_next_protos_advertised_cb)(SSL_CTX *, +                                              int (*)(SSL *, +                                                      const unsigned char **, +                                                      unsigned int *, +                                                      void *), +                                              void *) = NULL; +void (*SSL_CTX_set_next_proto_select_cb)(SSL_CTX *, +                                         int (*)(SSL *, +                                                 unsigned char **, +                                                 unsigned char *, +                                                 const unsigned char *, +                                                 unsigned int, +                                                 void *), +                                         void *) = NULL; +int (*SSL_select_next_proto)(unsigned char **, unsigned char *, +                             const unsigned char *, unsigned int, +                             const unsigned char *, unsigned int) = NULL; +void (*SSL_get0_next_proto_negotiated)(const SSL *, +                                       const unsigned char **, +                                       unsigned *) = NULL; +#else +static const long Cryptography_HAS_NEXTPROTONEG = 1; +#endif + +/* ALPN was added in OpenSSL 1.0.2. */ +#if OPENSSL_VERSION_NUMBER < 0x10002001L +int (*SSL_CTX_set_alpn_protos)(SSL_CTX *, +                               const unsigned char*, +                               unsigned) = NULL; +int (*SSL_set_alpn_protos)(SSL *, const unsigned char*, unsigned) = NULL; +void (*SSL_CTX_set_alpn_select_cb)(SSL_CTX *, +                                   int (*) (SSL *, +                                            const unsigned char **, +                                            unsigned char *, +                                            const unsigned char *, +                                            unsigned int, +                                            void *), +                                   void *) = NULL; +void (*SSL_get0_alpn_selected)(const SSL *, +                               const unsigned char **, +                               unsigned *) = NULL; +static const long Cryptography_HAS_ALPN = 0; +#else +static const long Cryptography_HAS_ALPN = 1; +#endif +""" + +CONDITIONAL_NAMES = { +    "Cryptography_HAS_TLSv1_1": [ +        "SSL_OP_NO_TLSv1_1", +        "TLSv1_1_method", +        "TLSv1_1_server_method", +        "TLSv1_1_client_method", +    ], + +    "Cryptography_HAS_TLSv1_2": [ +        "SSL_OP_NO_TLSv1_2", +        "TLSv1_2_method", +        "TLSv1_2_server_method", +        "TLSv1_2_client_method", +    ], + +    "Cryptography_HAS_SSL2": [ +        "SSLv2_method", +        "SSLv2_client_method", +        "SSLv2_server_method", +    ], + +    "Cryptography_HAS_TLSEXT_HOSTNAME": [ +        "SSL_set_tlsext_host_name", +        "SSL_get_servername", +        "SSL_CTX_set_tlsext_servername_callback", +    ], + +    "Cryptography_HAS_RELEASE_BUFFERS": [ +        "SSL_MODE_RELEASE_BUFFERS", +    ], + +    "Cryptography_HAS_OP_NO_COMPRESSION": [ +        "SSL_OP_NO_COMPRESSION", +    ], + +    "Cryptography_HAS_SSL_OP_MSIE_SSLV2_RSA_PADDING": [ +        "SSL_OP_MSIE_SSLV2_RSA_PADDING", +    ], + +    "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", +    ], + +    "Cryptography_HAS_NETBSD_D1_METH": [ +        "DTLSv1_method", +    ], + +    "Cryptography_HAS_NEXTPROTONEG": [ +        "SSL_CTX_set_next_protos_advertised_cb", +        "SSL_CTX_set_next_proto_select_cb", +        "SSL_select_next_proto", +        "SSL_get0_next_proto_negotiated", +    ], + +    "Cryptography_HAS_SECURE_RENEGOTIATION": [ +        "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION", +        "SSL_OP_LEGACY_SERVER_CONNECT", +        "SSL_get_secure_renegotiation_support", +    ], + +    "Cryptography_HAS_ALPN": [ +        "SSL_CTX_set_alpn_protos", +        "SSL_set_alpn_protos", +        "SSL_CTX_set_alpn_select_cb", +        "SSL_get0_alpn_selected", +    ] +} diff --git a/src/cryptography/hazmat/bindings/openssl/x509.py b/src/cryptography/hazmat/bindings/openssl/x509.py new file mode 100644 index 00000000..95bc7c3b --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/x509.py @@ -0,0 +1,273 @@ +# 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 + +INCLUDES = """ +#include <openssl/ssl.h> + +/* + * This is part of a work-around for the difficulty cffi has in dealing with + * `STACK_OF(foo)` as the name of a type.  We invent a new, simpler name that + * will be an alias for this type and use the alias throughout.  This works + * together with another opaque typedef for the same name in the TYPES section. + * Note that the result is an opaque type. + */ +typedef STACK_OF(X509) Cryptography_STACK_OF_X509; +typedef STACK_OF(X509_CRL) Cryptography_STACK_OF_X509_CRL; +typedef STACK_OF(X509_REVOKED) Cryptography_STACK_OF_X509_REVOKED; +""" + +TYPES = """ +typedef ... Cryptography_STACK_OF_X509; +typedef ... Cryptography_STACK_OF_X509_CRL; +typedef ... Cryptography_STACK_OF_X509_REVOKED; + +typedef struct { +    ASN1_OBJECT *algorithm; +    ...; +} X509_ALGOR; + +typedef ... X509_ATTRIBUTE; + +typedef struct { +    X509_ALGOR *signature; +    ...; +} X509_CINF; + +typedef struct { +    ASN1_OBJECT *object; +    ASN1_BOOLEAN critical; +    ASN1_OCTET_STRING *value; +} X509_EXTENSION; + +typedef ... X509_EXTENSIONS; + +typedef ... X509_REQ; + +typedef struct { +    ASN1_INTEGER *serialNumber; +    ASN1_TIME *revocationDate; +    X509_EXTENSIONS *extensions; +    int sequence; +    ...; +} X509_REVOKED; + +typedef struct { +    Cryptography_STACK_OF_X509_REVOKED *revoked; +    ...; +} X509_CRL_INFO; + +typedef struct { +    X509_CRL_INFO *crl; +    ...; +} X509_CRL; + +typedef struct { +    X509_CINF *cert_info; +    ...; +} X509; + +typedef ... NETSCAPE_SPKI; +""" + +FUNCTIONS = """ +X509 *X509_new(void); +void X509_free(X509 *); +X509 *X509_dup(X509 *); + +int X509_print_ex(BIO *, X509 *, unsigned long, unsigned long); + +int X509_set_version(X509 *, long); + +EVP_PKEY *X509_get_pubkey(X509 *); +int X509_set_pubkey(X509 *, EVP_PKEY *); + +unsigned char *X509_alias_get0(X509 *, int *); +int X509_sign(X509 *, EVP_PKEY *, const EVP_MD *); + +int X509_digest(const X509 *, const EVP_MD *, unsigned char *, unsigned int *); + +ASN1_TIME *X509_gmtime_adj(ASN1_TIME *, long); + +unsigned long X509_subject_name_hash(X509 *); + +X509_NAME *X509_get_subject_name(X509 *); +int X509_set_subject_name(X509 *, X509_NAME *); + +X509_NAME *X509_get_issuer_name(X509 *); +int X509_set_issuer_name(X509 *, X509_NAME *); + +int X509_get_ext_count(X509 *); +int X509_add_ext(X509 *, X509_EXTENSION *, int); +X509_EXTENSION *X509_EXTENSION_dup(X509_EXTENSION *); +X509_EXTENSION *X509_get_ext(X509 *, int); +int X509_EXTENSION_get_critical(X509_EXTENSION *); +ASN1_OBJECT *X509_EXTENSION_get_object(X509_EXTENSION *); +void X509_EXTENSION_free(X509_EXTENSION *); + +int X509_REQ_set_version(X509_REQ *, long); +X509_REQ *X509_REQ_new(void); +void X509_REQ_free(X509_REQ *); +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 *); +int X509_REQ_digest(const X509_REQ *, const EVP_MD *, +                    unsigned char *, unsigned int *); +EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *); +int X509_REQ_print_ex(BIO *, X509_REQ *, unsigned long, unsigned long); + +int X509V3_EXT_print(BIO *, X509_EXTENSION *, unsigned long, int); +ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *); + +X509_REVOKED *X509_REVOKED_new(void); +void X509_REVOKED_free(X509_REVOKED *); + +int X509_REVOKED_set_serialNumber(X509_REVOKED *, ASN1_INTEGER *); + +int X509_REVOKED_add1_ext_i2d(X509_REVOKED *, int, void *, int, unsigned long); + +X509_CRL *d2i_X509_CRL_bio(BIO *, X509_CRL **); +X509_CRL *X509_CRL_new(void); +void X509_CRL_free(X509_CRL *); +int X509_CRL_add0_revoked(X509_CRL *, X509_REVOKED *); +int i2d_X509_CRL_bio(BIO *, X509_CRL *); +int X509_CRL_print(BIO *, X509_CRL *); +int X509_CRL_set_issuer_name(X509_CRL *, X509_NAME *); +int X509_CRL_sign(X509_CRL *, EVP_PKEY *, const EVP_MD *); + +int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *, EVP_PKEY *); +int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *, EVP_PKEY *, const EVP_MD *); +char *NETSCAPE_SPKI_b64_encode(NETSCAPE_SPKI *); +EVP_PKEY *NETSCAPE_SPKI_get_pubkey(NETSCAPE_SPKI *); +int NETSCAPE_SPKI_set_pubkey(NETSCAPE_SPKI *, EVP_PKEY *); +NETSCAPE_SPKI *NETSCAPE_SPKI_new(void); +void NETSCAPE_SPKI_free(NETSCAPE_SPKI *); + +/*  ASN1 serialization */ +int i2d_X509_bio(BIO *, X509 *); +X509 *d2i_X509_bio(BIO *, X509 **); + +int i2d_X509_REQ_bio(BIO *, X509_REQ *); +X509_REQ *d2i_X509_REQ_bio(BIO *, X509_REQ **); + +int i2d_PrivateKey_bio(BIO *, EVP_PKEY *); +EVP_PKEY *d2i_PrivateKey_bio(BIO *, EVP_PKEY **); +int i2d_PUBKEY_bio(BIO *, EVP_PKEY *); +EVP_PKEY *d2i_PUBKEY_bio(BIO *, EVP_PKEY **); + +ASN1_INTEGER *X509_get_serialNumber(X509 *); +int X509_set_serialNumber(X509 *, ASN1_INTEGER *); + +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); + +int i2d_RSA_PUBKEY(RSA *, unsigned char **); +RSA *d2i_RSA_PUBKEY(RSA **, const unsigned char **, long); +RSA *d2i_RSAPublicKey(RSA **, const unsigned char **, long); +RSA *d2i_RSAPrivateKey(RSA **, const unsigned char **, long); +int i2d_DSA_PUBKEY(DSA *, unsigned char **); +DSA *d2i_DSA_PUBKEY(DSA **, const unsigned char **, long); +DSA *d2i_DSAPublicKey(DSA **, const unsigned char **, long); +DSA *d2i_DSAPrivateKey(DSA **, const unsigned char **, long); + +RSA *d2i_RSAPrivateKey_bio(BIO *, RSA **); +int i2d_RSAPrivateKey_bio(BIO *, RSA *); +RSA *d2i_RSAPublicKey_bio(BIO *, RSA **); +int i2d_RSAPublicKey_bio(BIO *, RSA *); +RSA *d2i_RSA_PUBKEY_bio(BIO *, RSA **); +int i2d_RSA_PUBKEY_bio(BIO *, RSA *); +DSA *d2i_DSA_PUBKEY_bio(BIO *, DSA **); +int i2d_DSA_PUBKEY_bio(BIO *, DSA *); +DSA *d2i_DSAPrivateKey_bio(BIO *, DSA **); +int i2d_DSAPrivateKey_bio(BIO *, DSA *); +""" + +MACROS = """ +long X509_get_version(X509 *); + +ASN1_TIME *X509_get_notBefore(X509 *); +ASN1_TIME *X509_get_notAfter(X509 *); + +long X509_REQ_get_version(X509_REQ *); +X509_NAME *X509_REQ_get_subject_name(X509_REQ *); + +Cryptography_STACK_OF_X509 *sk_X509_new_null(void); +void sk_X509_free(Cryptography_STACK_OF_X509 *); +int sk_X509_num(Cryptography_STACK_OF_X509 *); +int sk_X509_push(Cryptography_STACK_OF_X509 *, X509 *); +X509 *sk_X509_value(Cryptography_STACK_OF_X509 *, int); + +X509_EXTENSIONS *sk_X509_EXTENSION_new_null(void); +int sk_X509_EXTENSION_num(X509_EXTENSIONS *); +X509_EXTENSION *sk_X509_EXTENSION_value(X509_EXTENSIONS *, int); +int sk_X509_EXTENSION_push(X509_EXTENSIONS *, X509_EXTENSION *); +X509_EXTENSION *sk_X509_EXTENSION_delete(X509_EXTENSIONS *, int); +void sk_X509_EXTENSION_free(X509_EXTENSIONS *); + +int sk_X509_REVOKED_num(Cryptography_STACK_OF_X509_REVOKED *); +X509_REVOKED *sk_X509_REVOKED_value(Cryptography_STACK_OF_X509_REVOKED *, int); + +int i2d_RSAPublicKey(RSA *, unsigned char **); +int i2d_RSAPrivateKey(RSA *, unsigned char **); +int i2d_DSAPublicKey(DSA *, unsigned char **); +int i2d_DSAPrivateKey(DSA *, unsigned char **); + +/* 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 *); + +int i2d_EC_PUBKEY(EC_KEY *, unsigned char **); +EC_KEY *d2i_EC_PUBKEY(EC_KEY **, const unsigned char **, long); +EC_KEY *d2i_EC_PUBKEY_bio(BIO *, EC_KEY **); +int i2d_EC_PUBKEY_bio(BIO *, EC_KEY *); +EC_KEY *d2i_ECPrivateKey_bio(BIO *, EC_KEY **); +int i2d_ECPrivateKey_bio(BIO *, EC_KEY *); +""" + +CUSTOMIZATIONS = """ +/* OpenSSL 0.9.8e does not have this definition. */ +#if OPENSSL_VERSION_NUMBER <= 0x0090805fL +typedef STACK_OF(X509_EXTENSION) X509_EXTENSIONS; +#endif +#ifdef OPENSSL_NO_EC +int (*i2d_EC_PUBKEY)(EC_KEY *, unsigned char **) = NULL; +EC_KEY *(*d2i_EC_PUBKEY)(EC_KEY **, const unsigned char **, long) = NULL; +EC_KEY *(*d2i_EC_PUBKEY_bio)(BIO *, EC_KEY **) = NULL; +int (*i2d_EC_PUBKEY_bio)(BIO *, EC_KEY *) = NULL; +EC_KEY *(*d2i_ECPrivateKey_bio)(BIO *, EC_KEY **) = NULL; +int (*i2d_ECPrivateKey_bio)(BIO *, EC_KEY *) = NULL; +#endif +""" + +CONDITIONAL_NAMES = { +    "Cryptography_HAS_EC": [ +        "i2d_EC_PUBKEY", +        "d2i_EC_PUBKEY", +        "d2i_EC_PUBKEY_bio", +        "i2d_EC_PUBKEY_bio", +        "d2i_ECPrivateKey_bio", +        "i2d_ECPrivateKey_bio", +    ] +} diff --git a/src/cryptography/hazmat/bindings/openssl/x509_vfy.py b/src/cryptography/hazmat/bindings/openssl/x509_vfy.py new file mode 100644 index 00000000..601926c9 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/x509_vfy.py @@ -0,0 +1,336 @@ +# 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 + +INCLUDES = """ +#include <openssl/x509_vfy.h> + +/* + * This is part of a work-around for the difficulty cffi has in dealing with + * `STACK_OF(foo)` as the name of a type.  We invent a new, simpler name that + * will be an alias for this type and use the alias throughout.  This works + * together with another opaque typedef for the same name in the TYPES section. + * Note that the result is an opaque type. + */ +typedef STACK_OF(ASN1_OBJECT) Cryptography_STACK_OF_ASN1_OBJECT; +""" + +TYPES = """ +static const long Cryptography_HAS_102_VERIFICATION_ERROR_CODES; +static const long Cryptography_HAS_102_VERIFICATION_PARAMS; +static const long Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST; +static const long Cryptography_HAS_X509_V_FLAG_PARTIAL_CHAIN; +static const long Cryptography_HAS_100_VERIFICATION_ERROR_CODES; +static const long Cryptography_HAS_100_VERIFICATION_PARAMS; +static const long Cryptography_HAS_X509_V_FLAG_CHECK_SS_SIGNATURE; + +typedef ... Cryptography_STACK_OF_ASN1_OBJECT; + +typedef ... X509_STORE; +typedef ... X509_STORE_CTX; +typedef ... X509_VERIFY_PARAM; + +/* While these are defined in the source as ints, they're tagged here +   as longs, just in case they ever grow to large, such as what we saw +   with OP_ALL. */ + +/* Verification error codes */ +static const int X509_V_OK; +static const int X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; +static const int X509_V_ERR_UNABLE_TO_GET_CRL; +static const int X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE; +static const int X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE; +static const int X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY; +static const int X509_V_ERR_CERT_SIGNATURE_FAILURE; +static const int X509_V_ERR_CRL_SIGNATURE_FAILURE; +static const int X509_V_ERR_CERT_NOT_YET_VALID; +static const int X509_V_ERR_CERT_HAS_EXPIRED; +static const int X509_V_ERR_CRL_NOT_YET_VALID; +static const int X509_V_ERR_CRL_HAS_EXPIRED; +static const int X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD; +static const int X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD; +static const int X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD; +static const int X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD; +static const int X509_V_ERR_OUT_OF_MEM; +static const int X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; +static const int X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; +static const int X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY; +static const int X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE; +static const int X509_V_ERR_CERT_CHAIN_TOO_LONG; +static const int X509_V_ERR_CERT_REVOKED; +static const int X509_V_ERR_INVALID_CA; +static const int X509_V_ERR_PATH_LENGTH_EXCEEDED; +static const int X509_V_ERR_INVALID_PURPOSE; +static const int X509_V_ERR_CERT_UNTRUSTED; +static const int X509_V_ERR_CERT_REJECTED; +static const int X509_V_ERR_SUBJECT_ISSUER_MISMATCH; +static const int X509_V_ERR_AKID_SKID_MISMATCH; +static const int X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH; +static const int X509_V_ERR_KEYUSAGE_NO_CERTSIGN; +static const int X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER; +static const int X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION; +static const int X509_V_ERR_KEYUSAGE_NO_CRL_SIGN; +static const int X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION; +static const int X509_V_ERR_INVALID_NON_CA; +static const int X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED; +static const int X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE; +static const int X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED; +static const int X509_V_ERR_INVALID_EXTENSION; +static const int X509_V_ERR_INVALID_POLICY_EXTENSION; +static const int X509_V_ERR_NO_EXPLICIT_POLICY; +static const int X509_V_ERR_DIFFERENT_CRL_SCOPE; +static const int X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE; +static const int X509_V_ERR_UNNESTED_RESOURCE; +static const int X509_V_ERR_PERMITTED_VIOLATION; +static const int X509_V_ERR_EXCLUDED_VIOLATION; +static const int X509_V_ERR_SUBTREE_MINMAX; +static const int X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE; +static const int X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX; +static const int X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; +static const int X509_V_ERR_CRL_PATH_VALIDATION_ERROR; +static const int X509_V_ERR_SUITE_B_INVALID_VERSION; +static const int X509_V_ERR_SUITE_B_INVALID_ALGORITHM; +static const int X509_V_ERR_SUITE_B_INVALID_CURVE; +static const int X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM; +static const int X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED; +static const int X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256; +static const int X509_V_ERR_HOSTNAME_MISMATCH; +static const int X509_V_ERR_EMAIL_MISMATCH; +static const int X509_V_ERR_IP_ADDRESS_MISMATCH; +static const int X509_V_ERR_APPLICATION_VERIFICATION; + +/* Verification parameters */ +static const long X509_V_FLAG_CB_ISSUER_CHECK; +static const long X509_V_FLAG_USE_CHECK_TIME; +static const long X509_V_FLAG_CRL_CHECK; +static const long X509_V_FLAG_CRL_CHECK_ALL; +static const long X509_V_FLAG_IGNORE_CRITICAL; +static const long X509_V_FLAG_X509_STRICT; +static const long X509_V_FLAG_ALLOW_PROXY_CERTS; +static const long X509_V_FLAG_POLICY_CHECK; +static const long X509_V_FLAG_EXPLICIT_POLICY; +static const long X509_V_FLAG_INHIBIT_ANY; +static const long X509_V_FLAG_INHIBIT_MAP; +static const long X509_V_FLAG_NOTIFY_POLICY; +static const long X509_V_FLAG_EXTENDED_CRL_SUPPORT; +static const long X509_V_FLAG_USE_DELTAS; +static const long X509_V_FLAG_CHECK_SS_SIGNATURE; +static const long X509_V_FLAG_TRUSTED_FIRST; +static const long X509_V_FLAG_SUITEB_128_LOS_ONLY; +static const long X509_V_FLAG_SUITEB_192_LOS; +static const long X509_V_FLAG_SUITEB_128_LOS; +static const long X509_V_FLAG_PARTIAL_CHAIN; +""" + +FUNCTIONS = """ +int X509_verify_cert(X509_STORE_CTX *); + +/* X509_STORE */ +X509_STORE *X509_STORE_new(void); +void X509_STORE_free(X509_STORE *); +int X509_STORE_add_cert(X509_STORE *, X509 *); + +/* X509_STORE_CTX */ +X509_STORE_CTX *X509_STORE_CTX_new(void); +void X509_STORE_CTX_cleanup(X509_STORE_CTX *); +void X509_STORE_CTX_free(X509_STORE_CTX *); +int X509_STORE_CTX_init(X509_STORE_CTX *, X509_STORE *, X509 *, +                        Cryptography_STACK_OF_X509 *); +void X509_STORE_CTX_trusted_stack(X509_STORE_CTX *, +                                  Cryptography_STACK_OF_X509 *); +void X509_STORE_CTX_set_cert(X509_STORE_CTX *, X509 *); +void X509_STORE_CTX_set_chain(X509_STORE_CTX *,Cryptography_STACK_OF_X509 *); +X509_VERIFY_PARAM *X509_STORE_CTX_get0_param(X509_STORE_CTX *); +void X509_STORE_CTX_set0_param(X509_STORE_CTX *, X509_VERIFY_PARAM *); +int X509_STORE_CTX_set_default(X509_STORE_CTX *, const char *); +void X509_STORE_CTX_set_verify_cb(X509_STORE_CTX *, +                                  int (*)(int, X509_STORE_CTX *)); +Cryptography_STACK_OF_X509 *X509_STORE_CTX_get_chain(X509_STORE_CTX *); +Cryptography_STACK_OF_X509 *X509_STORE_CTX_get1_chain(X509_STORE_CTX *); +int X509_STORE_CTX_get_error(X509_STORE_CTX *); +void X509_STORE_CTX_set_error(X509_STORE_CTX *, int); +int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *); +X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *); +int X509_STORE_CTX_set_ex_data(X509_STORE_CTX *, int, void *); +void *X509_STORE_CTX_get_ex_data(X509_STORE_CTX *, int); + +/* X509_VERIFY_PARAM */ +X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void); +int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *, unsigned long); +int X509_VERIFY_PARAM_clear_flags(X509_VERIFY_PARAM *, unsigned long); +unsigned long X509_VERIFY_PARAM_get_flags(X509_VERIFY_PARAM *); +int X509_VERIFY_PARAM_set_purpose(X509_VERIFY_PARAM *, int); +int X509_VERIFY_PARAM_set_trust(X509_VERIFY_PARAM *, int); +void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *, time_t); +int X509_VERIFY_PARAM_add0_policy(X509_VERIFY_PARAM *, ASN1_OBJECT *); +int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *, +                                    Cryptography_STACK_OF_ASN1_OBJECT *); +void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *, int); +int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *); +""" + +MACROS = """ +/* X509_STORE_CTX */ +void X509_STORE_CTX_set0_crls(X509_STORE_CTX *, +                              Cryptography_STACK_OF_X509_CRL *); + +/* X509_VERIFY_PARAM */ +int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *, const char *, +                                size_t); +void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *, unsigned int); +int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *, const char *, +                                 size_t); +int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *, const unsigned char *, +                              size_t); +int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *, const char *); +""" + +CUSTOMIZATIONS = """ +/* OpenSSL 1.0.2+ verification error codes */ +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +static const long Cryptography_HAS_102_VERIFICATION_ERROR_CODES = 1; +#else +static const long Cryptography_HAS_102_VERIFICATION_ERROR_CODES = 0; +static const long X509_V_ERR_SUITE_B_INVALID_VERSION = 0; +static const long X509_V_ERR_SUITE_B_INVALID_ALGORITHM = 0; +static const long X509_V_ERR_SUITE_B_INVALID_CURVE = 0; +static const long X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM = 0; +static const long X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED = 0; +static const long X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 = 0; +static const long X509_V_ERR_HOSTNAME_MISMATCH = 0; +static const long X509_V_ERR_EMAIL_MISMATCH = 0; +static const long X509_V_ERR_IP_ADDRESS_MISMATCH = 0; +#endif + +/* OpenSSL 1.0.2+ verification parameters */ +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +static const long Cryptography_HAS_102_VERIFICATION_PARAMS = 1; +#else +static const long Cryptography_HAS_102_VERIFICATION_PARAMS = 0; +/* X509_V_FLAG_TRUSTED_FIRST is also new in 1.0.2+, but it is added separately +   below because it shows up in some earlier 3rd party OpenSSL packages. */ +static const long X509_V_FLAG_SUITEB_128_LOS_ONLY = 0; +static const long X509_V_FLAG_SUITEB_192_LOS = 0; +static const long X509_V_FLAG_SUITEB_128_LOS = 0; + +int (*X509_VERIFY_PARAM_set1_host)(X509_VERIFY_PARAM *, const char *, +                                   size_t) = NULL; +int (*X509_VERIFY_PARAM_set1_email)(X509_VERIFY_PARAM *, const char *, +                                    size_t) = NULL; +int (*X509_VERIFY_PARAM_set1_ip)(X509_VERIFY_PARAM *, const unsigned char *, +                                 size_t) = NULL; +int (*X509_VERIFY_PARAM_set1_ip_asc)(X509_VERIFY_PARAM *, const char *) = NULL; +void (*X509_VERIFY_PARAM_set_hostflags)(X509_VERIFY_PARAM *, +                                        unsigned int) = NULL; +#endif + +/* OpenSSL 1.0.2+ or Solaris's backport */ +#ifdef X509_V_FLAG_PARTIAL_CHAIN +static const long Cryptography_HAS_X509_V_FLAG_PARTIAL_CHAIN = 1; +#else +static const long Cryptography_HAS_X509_V_FLAG_PARTIAL_CHAIN = 0; +static const long X509_V_FLAG_PARTIAL_CHAIN = 0; +#endif + +/* OpenSSL 1.0.2+, *or* Fedora 20's flavor of OpenSSL 1.0.1e... */ +#ifdef X509_V_FLAG_TRUSTED_FIRST +static const long Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST = 1; +#else +static const long Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST = 0; +static const long X509_V_FLAG_TRUSTED_FIRST = 0; +#endif + +/* OpenSSL 1.0.0+ verification error codes */ +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +static const long Cryptography_HAS_100_VERIFICATION_ERROR_CODES = 1; +#else +static const long Cryptography_HAS_100_VERIFICATION_ERROR_CODES = 0; +static const long X509_V_ERR_DIFFERENT_CRL_SCOPE = 0; +static const long X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE = 0; +static const long X509_V_ERR_PERMITTED_VIOLATION = 0; +static const long X509_V_ERR_EXCLUDED_VIOLATION = 0; +static const long X509_V_ERR_SUBTREE_MINMAX = 0; +static const long X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE = 0; +static const long X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX = 0; +static const long X509_V_ERR_UNSUPPORTED_NAME_SYNTAX = 0; +static const long X509_V_ERR_CRL_PATH_VALIDATION_ERROR = 0; +#endif + +/* OpenSSL 1.0.0+ verification parameters */ +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +static const long Cryptography_HAS_100_VERIFICATION_PARAMS = 1; +#else +static const long Cryptography_HAS_100_VERIFICATION_PARAMS = 0; +static const long X509_V_FLAG_EXTENDED_CRL_SUPPORT = 0; +static const long X509_V_FLAG_USE_DELTAS = 0; +#endif + +/* OpenSSL 0.9.8recent+ */ +#ifdef X509_V_FLAG_CHECK_SS_SIGNATURE +static const long Cryptography_HAS_X509_V_FLAG_CHECK_SS_SIGNATURE = 1; +#else +static const long Cryptography_HAS_X509_V_FLAG_CHECK_SS_SIGNATURE = 0; +static const long X509_V_FLAG_CHECK_SS_SIGNATURE = 0; +#endif +""" + +CONDITIONAL_NAMES = { +    "Cryptography_HAS_102_VERIFICATION_ERROR_CODES": [ +        'X509_V_ERR_SUITE_B_INVALID_VERSION', +        'X509_V_ERR_SUITE_B_INVALID_ALGORITHM', +        'X509_V_ERR_SUITE_B_INVALID_CURVE', +        'X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM', +        'X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED', +        'X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256', +        'X509_V_ERR_HOSTNAME_MISMATCH', +        'X509_V_ERR_EMAIL_MISMATCH', +        'X509_V_ERR_IP_ADDRESS_MISMATCH' +    ], +    "Cryptography_HAS_102_VERIFICATION_PARAMS": [ +        "X509_V_FLAG_SUITEB_128_LOS_ONLY", +        "X509_V_FLAG_SUITEB_192_LOS", +        "X509_V_FLAG_SUITEB_128_LOS", +        "X509_VERIFY_PARAM_set1_host", +        "X509_VERIFY_PARAM_set1_email", +        "X509_VERIFY_PARAM_set1_ip", +        "X509_VERIFY_PARAM_set1_ip_asc", +        "X509_VERIFY_PARAM_set_hostflags", +    ], +    "Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST": [ +        "X509_V_FLAG_TRUSTED_FIRST", +    ], +    "Cryptography_HAS_X509_V_FLAG_PARTIAL_CHAIN": [ +        "X509_V_FLAG_PARTIAL_CHAIN", +    ], +    "Cryptography_HAS_100_VERIFICATION_ERROR_CODES": [ +        'X509_V_ERR_DIFFERENT_CRL_SCOPE', +        'X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE', +        'X509_V_ERR_UNNESTED_RESOURCE', +        'X509_V_ERR_PERMITTED_VIOLATION', +        'X509_V_ERR_EXCLUDED_VIOLATION', +        'X509_V_ERR_SUBTREE_MINMAX', +        'X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE', +        'X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX', +        'X509_V_ERR_UNSUPPORTED_NAME_SYNTAX', +        'X509_V_ERR_CRL_PATH_VALIDATION_ERROR', +    ], +    "Cryptography_HAS_100_VERIFICATION_PARAMS": [ +        "Cryptography_HAS_100_VERIFICATION_PARAMS", +        "X509_V_FLAG_EXTENDED_CRL_SUPPORT", +        "X509_V_FLAG_USE_DELTAS", +    ], +    "Cryptography_HAS_X509_V_FLAG_CHECK_SS_SIGNATURE": [ +        "X509_V_FLAG_CHECK_SS_SIGNATURE", +    ] +} diff --git a/src/cryptography/hazmat/bindings/openssl/x509name.py b/src/cryptography/hazmat/bindings/openssl/x509name.py new file mode 100644 index 00000000..50abee2a --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/x509name.py @@ -0,0 +1,61 @@ +# 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 + +INCLUDES = """ +#include <openssl/x509.h> + +/* + * See the comment above Cryptography_STACK_OF_X509 in x509.py + */ +typedef STACK_OF(X509_NAME) Cryptography_STACK_OF_X509_NAME; +""" + +TYPES = """ +typedef ... X509_NAME; +typedef ... X509_NAME_ENTRY; +typedef ... Cryptography_STACK_OF_X509_NAME; +""" + +FUNCTIONS = """ +int X509_NAME_entry_count(X509_NAME *); +X509_NAME_ENTRY *X509_NAME_get_entry(X509_NAME *, int); +ASN1_OBJECT *X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *); +ASN1_STRING *X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *); +unsigned long X509_NAME_hash(X509_NAME *); + +int i2d_X509_NAME(X509_NAME *, unsigned char **); +int X509_NAME_add_entry_by_NID(X509_NAME *, int, int, unsigned char *, +                               int, int, int); +X509_NAME_ENTRY *X509_NAME_delete_entry(X509_NAME *, int); +void X509_NAME_ENTRY_free(X509_NAME_ENTRY *); +int X509_NAME_get_index_by_NID(X509_NAME *, int, int); +int X509_NAME_cmp(const X509_NAME *, const X509_NAME *); +char *X509_NAME_oneline(X509_NAME *, char *, int); +X509_NAME *X509_NAME_dup(X509_NAME *); +void X509_NAME_free(X509_NAME *); +""" + +MACROS = """ +Cryptography_STACK_OF_X509_NAME *sk_X509_NAME_new_null(void); +int sk_X509_NAME_num(Cryptography_STACK_OF_X509_NAME *); +int sk_X509_NAME_push(Cryptography_STACK_OF_X509_NAME *, X509_NAME *); +X509_NAME *sk_X509_NAME_value(Cryptography_STACK_OF_X509_NAME *, int); +void sk_X509_NAME_free(Cryptography_STACK_OF_X509_NAME *); +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/x509v3.py b/src/cryptography/hazmat/bindings/openssl/x509v3.py new file mode 100644 index 00000000..cf4be1fe --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/x509v3.py @@ -0,0 +1,103 @@ +# 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 + +INCLUDES = """ +#include <openssl/x509v3.h> +""" + +TYPES = """ +typedef struct { +    X509 *issuer_cert; +    X509 *subject_cert; +    ...; +} X509V3_CTX; + +typedef void * (*X509V3_EXT_D2I)(void *, const unsigned char **, long); + +typedef struct { +    ASN1_ITEM_EXP *it; +    X509V3_EXT_D2I d2i; +    ...; +} X509V3_EXT_METHOD; + +static const int GEN_OTHERNAME; +static const int GEN_EMAIL; +static const int GEN_X400; +static const int GEN_DNS; +static const int GEN_URI; +static const int GEN_DIRNAME; +static const int GEN_EDIPARTY; +static const int GEN_IPADD; +static const int GEN_RID; + +typedef struct { +    ...; +} OTHERNAME; + +typedef struct { +    ...; +} EDIPARTYNAME; + +typedef struct { +    int type; +    union { +        char *ptr; +        OTHERNAME *otherName;  /* otherName */ +        ASN1_IA5STRING *rfc822Name; +        ASN1_IA5STRING *dNSName; +        ASN1_TYPE *x400Address; +        X509_NAME *directoryName; +        EDIPARTYNAME *ediPartyName; +        ASN1_IA5STRING *uniformResourceIdentifier; +        ASN1_OCTET_STRING *iPAddress; +        ASN1_OBJECT *registeredID; + +        /* Old names */ +        ASN1_OCTET_STRING *ip; /* iPAddress */ +        X509_NAME *dirn;       /* dirn */ +        ASN1_IA5STRING *ia5;   /* rfc822Name, dNSName, */ +                               /*   uniformResourceIdentifier */ +        ASN1_OBJECT *rid;      /* registeredID */ +        ASN1_TYPE *other;      /* x400Address */ +    } d; +    ...; +} GENERAL_NAME; + +typedef struct stack_st_GENERAL_NAME GENERAL_NAMES; +""" + +FUNCTIONS = """ +void X509V3_set_ctx(X509V3_CTX *, X509 *, X509 *, X509_REQ *, X509_CRL *, int); +X509_EXTENSION *X509V3_EXT_nconf(CONF *, X509V3_CTX *, char *, char *); +int GENERAL_NAME_print(BIO *, GENERAL_NAME *); +void GENERAL_NAMES_free(GENERAL_NAMES *); +void *X509V3_EXT_d2i(X509_EXTENSION *); +""" + +MACROS = """ +void *X509V3_set_ctx_nodb(X509V3_CTX *); +int sk_GENERAL_NAME_num(struct stack_st_GENERAL_NAME *); +int sk_GENERAL_NAME_push(struct stack_st_GENERAL_NAME *, GENERAL_NAME *); +GENERAL_NAME *sk_GENERAL_NAME_value(struct stack_st_GENERAL_NAME *, int); + +/* These aren't macros these functions are all const X on openssl > 1.0.x */ +const X509V3_EXT_METHOD *X509V3_EXT_get(X509_EXTENSION *); +const X509V3_EXT_METHOD *X509V3_EXT_get_nid(int); +""" + +CUSTOMIZATIONS = """ +""" + +CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/utils.py b/src/cryptography/hazmat/bindings/utils.py new file mode 100644 index 00000000..55b61292 --- /dev/null +++ b/src/cryptography/hazmat/bindings/utils.py @@ -0,0 +1,144 @@ +# 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 binascii +import sys +import threading + +from cffi import FFI +from cffi.verifier import Verifier + + +class LazyLibrary(object): +    def __init__(self, ffi): +        self._ffi = ffi +        self._lib = None +        self._lock = threading.Lock() + +    def __getattr__(self, name): +        if self._lib is None: +            with self._lock: +                if self._lib is None: +                    self._lib = self._ffi.verifier.load_library() + +        return getattr(self._lib, name) + + +def load_library_for_binding(ffi, module_prefix, modules): +    lib = ffi.verifier.load_library() + +    for name in modules: +        module_name = module_prefix + name +        module = sys.modules[module_name] +        for condition, names in module.CONDITIONAL_NAMES.items(): +            if not getattr(lib, condition): +                for name in names: +                    delattr(lib, name) + +    return lib + + +def build_ffi_for_binding(module_prefix, modules, pre_include="", +                          post_include="", libraries=[], extra_compile_args=[], +                          extra_link_args=[]): +    """ +    Modules listed in ``modules`` should have the following attributes: + +    * ``INCLUDES``: A string containing C includes. +    * ``TYPES``: A string containing C declarations for types. +    * ``FUNCTIONS``: A string containing C declarations for functions. +    * ``MACROS``: A string containing C declarations for any macros. +    * ``CUSTOMIZATIONS``: A string containing arbitrary top-level C code, this +        can be used to do things like test for a define and provide an +        alternate implementation based on that. +    * ``CONDITIONAL_NAMES``: A dict mapping strings of condition names from the +        library to a list of names which will not be present without the +        condition. +    """ +    types = [] +    includes = [] +    functions = [] +    macros = [] +    customizations = [] +    for name in modules: +        module_name = module_prefix + name +        __import__(module_name) +        module = sys.modules[module_name] + +        types.append(module.TYPES) +        macros.append(module.MACROS) +        functions.append(module.FUNCTIONS) +        includes.append(module.INCLUDES) +        customizations.append(module.CUSTOMIZATIONS) + +    # We include functions here so that if we got any of their definitions +    # wrong, the underlying C compiler will explode. In C you are allowed +    # to re-declare a function if it has the same signature. That is: +    #   int foo(int); +    #   int foo(int); +    # is legal, but the following will fail to compile: +    #   int foo(int); +    #   int foo(short); +    verify_source = "\n".join( +        [pre_include] + +        includes + +        [post_include] + +        functions + +        customizations +    ) +    ffi = build_ffi( +        cdef_source="\n".join(types + functions + macros), +        verify_source=verify_source, +        libraries=libraries, +        extra_compile_args=extra_compile_args, +        extra_link_args=extra_link_args, +    ) + +    return ffi + + +def build_ffi(cdef_source, verify_source, libraries=[], extra_compile_args=[], +              extra_link_args=[]): +    ffi = FFI() +    ffi.cdef(cdef_source) + +    ffi.verifier = Verifier( +        ffi, +        verify_source, +        tmpdir='', +        modulename=_create_modulename(cdef_source, verify_source, sys.version), +        libraries=libraries, +        ext_package="cryptography", +        extra_compile_args=extra_compile_args, +        extra_link_args=extra_link_args, +    ) +    return ffi + + +def _create_modulename(cdef_sources, source, sys_version): +    """ +    cffi creates a modulename internally that incorporates the cffi version. +    This will cause cryptography's wheels to break when the version of cffi +    the user has does not match what was used when building the wheel. To +    resolve this we build our own modulename that uses most of the same code +    from cffi but elides the version key. +    """ +    key = '\x00'.join([sys_version[:3], source, cdef_sources]) +    key = key.encode('utf-8') +    k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff) +    k1 = k1.lstrip('0x').rstrip('L') +    k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff) +    k2 = k2.lstrip('0').rstrip('L') +    return '_Cryptography_cffi_{0}{1}'.format(k1, k2) diff --git a/src/cryptography/hazmat/primitives/__init__.py b/src/cryptography/hazmat/primitives/__init__.py new file mode 100644 index 00000000..2f420574 --- /dev/null +++ b/src/cryptography/hazmat/primitives/__init__.py @@ -0,0 +1,14 @@ +# 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 diff --git a/src/cryptography/hazmat/primitives/asymmetric/__init__.py b/src/cryptography/hazmat/primitives/asymmetric/__init__.py new file mode 100644 index 00000000..2f420574 --- /dev/null +++ b/src/cryptography/hazmat/primitives/asymmetric/__init__.py @@ -0,0 +1,14 @@ +# 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 diff --git a/src/cryptography/hazmat/primitives/asymmetric/dsa.py b/src/cryptography/hazmat/primitives/asymmetric/dsa.py new file mode 100644 index 00000000..83e01377 --- /dev/null +++ b/src/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -0,0 +1,108 @@ +# 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 + + +def generate_parameters(key_size, backend): +    return backend.generate_dsa_parameters(key_size) + + +def generate_private_key(key_size, backend): +    return backend.generate_dsa_private_key_and_parameters(key_size) + + +def _check_dsa_parameters(parameters): +    if utils.bit_length(parameters.p) not in [1024, 2048, 3072]: +        raise ValueError("p must be exactly 1024, 2048, or 3072 bits long") +    if utils.bit_length(parameters.q) not in [160, 256]: +        raise ValueError("q must be exactly 160 or 256 bits long") + +    if not (1 < parameters.g < parameters.p): +        raise ValueError("g, p don't satisfy 1 < g < p.") + + +def _check_dsa_private_numbers(numbers): +    parameters = numbers.public_numbers.parameter_numbers +    _check_dsa_parameters(parameters) +    if numbers.x <= 0 or numbers.x >= parameters.q: +        raise ValueError("x must be > 0 and < q.") + +    if numbers.public_numbers.y != pow(parameters.g, numbers.x, parameters.p): +        raise ValueError("y must be equal to (g ** x % p).") + + +class DSAParameterNumbers(object): +    def __init__(self, p, q, g): +        if ( +            not isinstance(p, six.integer_types) or +            not isinstance(q, six.integer_types) or +            not isinstance(g, six.integer_types) +        ): +            raise TypeError( +                "DSAParameterNumbers p, q, and g arguments must be integers." +            ) + +        self._p = p +        self._q = q +        self._g = g + +    p = utils.read_only_property("_p") +    q = utils.read_only_property("_q") +    g = utils.read_only_property("_g") + +    def parameters(self, backend): +        return backend.load_dsa_parameter_numbers(self) + + +class DSAPublicNumbers(object): +    def __init__(self, y, parameter_numbers): +        if not isinstance(y, six.integer_types): +            raise TypeError("DSAPublicNumbers y argument must be an integer.") + +        if not isinstance(parameter_numbers, DSAParameterNumbers): +            raise TypeError( +                "parameter_numbers must be a DSAParameterNumbers instance." +            ) + +        self._y = y +        self._parameter_numbers = parameter_numbers + +    y = utils.read_only_property("_y") +    parameter_numbers = utils.read_only_property("_parameter_numbers") + +    def public_key(self, backend): +        return backend.load_dsa_public_numbers(self) + + +class DSAPrivateNumbers(object): +    def __init__(self, x, public_numbers): +        if not isinstance(x, six.integer_types): +            raise TypeError("DSAPrivateNumbers x argument must be an integer.") + +        if not isinstance(public_numbers, DSAPublicNumbers): +            raise TypeError( +                "public_numbers must be a DSAPublicNumbers instance." +            ) +        self._public_numbers = public_numbers +        self._x = x + +    x = utils.read_only_property("_x") +    public_numbers = utils.read_only_property("_public_numbers") + +    def private_key(self, backend): +        return backend.load_dsa_private_numbers(self) diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py new file mode 100644 index 00000000..ced732fb --- /dev/null +++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py @@ -0,0 +1,195 @@ +# 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.hazmat.primitives import interfaces + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT571R1(object): +    name = "sect571r1" +    key_size = 571 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT409R1(object): +    name = "sect409r1" +    key_size = 409 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT283R1(object): +    name = "sect283r1" +    key_size = 283 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT233R1(object): +    name = "sect233r1" +    key_size = 233 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT163R2(object): +    name = "sect163r2" +    key_size = 163 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT571K1(object): +    name = "sect571k1" +    key_size = 571 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT409K1(object): +    name = "sect409k1" +    key_size = 409 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT283K1(object): +    name = "sect283k1" +    key_size = 283 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT233K1(object): +    name = "sect233k1" +    key_size = 233 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT163K1(object): +    name = "sect163k1" +    key_size = 163 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECP521R1(object): +    name = "secp521r1" +    key_size = 521 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECP384R1(object): +    name = "secp384r1" +    key_size = 384 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECP256R1(object): +    name = "secp256r1" +    key_size = 256 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECP224R1(object): +    name = "secp224r1" +    key_size = 224 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECP192R1(object): +    name = "secp192r1" +    key_size = 192 + + +_CURVE_TYPES = { +    "prime192v1": SECP192R1, +    "prime256v1": SECP256R1, + +    "secp192r1": SECP192R1, +    "secp224r1": SECP224R1, +    "secp256r1": SECP256R1, +    "secp384r1": SECP384R1, +    "secp521r1": SECP521R1, + +    "sect163k1": SECT163K1, +    "sect233k1": SECT233K1, +    "sect283k1": SECT283K1, +    "sect409k1": SECT409K1, +    "sect571k1": SECT571K1, + +    "sect163r2": SECT163R2, +    "sect233r1": SECT233R1, +    "sect283r1": SECT283R1, +    "sect409r1": SECT409R1, +    "sect571r1": SECT571R1, +} + + +@utils.register_interface(interfaces.EllipticCurveSignatureAlgorithm) +class ECDSA(object): +    def __init__(self, algorithm): +        self._algorithm = algorithm + +    algorithm = utils.read_only_property("_algorithm") + + +def generate_private_key(curve, backend): +    return backend.generate_elliptic_curve_private_key(curve) + + +class EllipticCurvePublicNumbers(object): +    def __init__(self, x, y, curve): +        if ( +            not isinstance(x, six.integer_types) or +            not isinstance(y, six.integer_types) +        ): +            raise TypeError("x and y must be integers.") + +        if not isinstance(curve, interfaces.EllipticCurve): +            raise TypeError("curve must provide the EllipticCurve interface.") + +        self._y = y +        self._x = x +        self._curve = curve + +    def public_key(self, backend): +        try: +            return backend.load_elliptic_curve_public_numbers(self) +        except AttributeError: +            return backend.elliptic_curve_public_key_from_numbers(self) + +    curve = utils.read_only_property("_curve") +    x = utils.read_only_property("_x") +    y = utils.read_only_property("_y") + + +class EllipticCurvePrivateNumbers(object): +    def __init__(self, private_value, public_numbers): +        if not isinstance(private_value, six.integer_types): +            raise TypeError("private_value must be an integer.") + +        if not isinstance(public_numbers, EllipticCurvePublicNumbers): +            raise TypeError( +                "public_numbers must be an EllipticCurvePublicNumbers " +                "instance." +            ) + +        self._private_value = private_value +        self._public_numbers = public_numbers + +    def private_key(self, backend): +        try: +            return backend.load_elliptic_curve_private_numbers(self) +        except AttributeError: +            return backend.elliptic_curve_private_key_from_numbers(self) + +    private_value = utils.read_only_property("_private_value") +    public_numbers = utils.read_only_property("_public_numbers") diff --git a/src/cryptography/hazmat/primitives/asymmetric/padding.py b/src/cryptography/hazmat/primitives/asymmetric/padding.py new file mode 100644 index 00000000..3967e065 --- /dev/null +++ b/src/cryptography/hazmat/primitives/asymmetric/padding.py @@ -0,0 +1,65 @@ +# 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.hazmat.primitives import interfaces + + +@utils.register_interface(interfaces.AsymmetricPadding) +class PKCS1v15(object): +    name = "EMSA-PKCS1-v1_5" + + +@utils.register_interface(interfaces.AsymmetricPadding) +class PSS(object): +    MAX_LENGTH = object() +    name = "EMSA-PSS" + +    def __init__(self, mgf, salt_length): +        self._mgf = mgf + +        if (not isinstance(salt_length, six.integer_types) and +                salt_length is not self.MAX_LENGTH): +            raise TypeError("salt_length must be an integer.") + +        if salt_length is not self.MAX_LENGTH and salt_length < 0: +            raise ValueError("salt_length must be zero or greater.") + +        self._salt_length = salt_length + + +@utils.register_interface(interfaces.AsymmetricPadding) +class OAEP(object): +    name = "EME-OAEP" + +    def __init__(self, mgf, algorithm, label): +        if not isinstance(algorithm, interfaces.HashAlgorithm): +            raise TypeError("Expected instance of interfaces.HashAlgorithm.") + +        self._mgf = mgf +        self._algorithm = algorithm +        self._label = label + + +class MGF1(object): +    MAX_LENGTH = object() + +    def __init__(self, algorithm): +        if not isinstance(algorithm, interfaces.HashAlgorithm): +            raise TypeError("Expected instance of interfaces.HashAlgorithm.") + +        self._algorithm = algorithm diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py new file mode 100644 index 00000000..db38ed55 --- /dev/null +++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -0,0 +1,191 @@ +# 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 UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.backends.interfaces import RSABackend + + +def generate_private_key(public_exponent, key_size, backend): +    if not isinstance(backend, RSABackend): +        raise UnsupportedAlgorithm( +            "Backend object does not implement RSABackend.", +            _Reasons.BACKEND_MISSING_INTERFACE +        ) + +    _verify_rsa_parameters(public_exponent, key_size) +    return backend.generate_rsa_private_key(public_exponent, key_size) + + +def _verify_rsa_parameters(public_exponent, key_size): +    if public_exponent < 3: +        raise ValueError("public_exponent must be >= 3.") + +    if public_exponent & 1 == 0: +        raise ValueError("public_exponent must be odd.") + +    if key_size < 512: +        raise ValueError("key_size must be at least 512-bits.") + + +def _check_private_key_components(p, q, private_exponent, dmp1, dmq1, iqmp, +                                  public_exponent, modulus): +    if modulus < 3: +        raise ValueError("modulus must be >= 3.") + +    if p >= modulus: +        raise ValueError("p must be < modulus.") + +    if q >= modulus: +        raise ValueError("q must be < modulus.") + +    if dmp1 >= modulus: +        raise ValueError("dmp1 must be < modulus.") + +    if dmq1 >= modulus: +        raise ValueError("dmq1 must be < modulus.") + +    if iqmp >= modulus: +        raise ValueError("iqmp must be < modulus.") + +    if private_exponent >= modulus: +        raise ValueError("private_exponent must be < modulus.") + +    if public_exponent < 3 or public_exponent >= modulus: +        raise ValueError("public_exponent must be >= 3 and < modulus.") + +    if public_exponent & 1 == 0: +        raise ValueError("public_exponent must be odd.") + +    if dmp1 & 1 == 0: +        raise ValueError("dmp1 must be odd.") + +    if dmq1 & 1 == 0: +        raise ValueError("dmq1 must be odd.") + +    if p * q != modulus: +        raise ValueError("p*q must equal modulus.") + + +def _check_public_key_components(e, n): +    if n < 3: +        raise ValueError("n must be >= 3.") + +    if e < 3 or e >= n: +        raise ValueError("e must be >= 3 and < n.") + +    if e & 1 == 0: +        raise ValueError("e must be odd.") + + +def _modinv(e, m): +    """ +    Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1 +    """ +    x1, y1, x2, y2 = 1, 0, 0, 1 +    a, b = e, m +    while b > 0: +        q, r = divmod(a, b) +        xn, yn = x1 - q * x2, y1 - q * y2 +        a, b, x1, y1, x2, y2 = b, r, x2, y2, xn, yn +    return x1 % m + + +def rsa_crt_iqmp(p, q): +    """ +    Compute the CRT (q ** -1) % p value from RSA primes p and q. +    """ +    return _modinv(q, p) + + +def rsa_crt_dmp1(private_exponent, p): +    """ +    Compute the CRT private_exponent % (p - 1) value from the RSA +    private_exponent and p. +    """ +    return private_exponent % (p - 1) + + +def rsa_crt_dmq1(private_exponent, q): +    """ +    Compute the CRT private_exponent % (q - 1) value from the RSA +    private_exponent and q. +    """ +    return private_exponent % (q - 1) + + +class RSAPrivateNumbers(object): +    def __init__(self, p, q, d, dmp1, dmq1, iqmp, +                 public_numbers): +        if ( +            not isinstance(p, six.integer_types) or +            not isinstance(q, six.integer_types) or +            not isinstance(d, six.integer_types) or +            not isinstance(dmp1, six.integer_types) or +            not isinstance(dmq1, six.integer_types) or +            not isinstance(iqmp, six.integer_types) +        ): +            raise TypeError( +                "RSAPrivateNumbers p, q, d, dmp1, dmq1, iqmp arguments must" +                " all be an integers." +            ) + +        if not isinstance(public_numbers, RSAPublicNumbers): +            raise TypeError( +                "RSAPrivateNumbers public_numbers must be an RSAPublicNumbers" +                " instance." +            ) + +        self._p = p +        self._q = q +        self._d = d +        self._dmp1 = dmp1 +        self._dmq1 = dmq1 +        self._iqmp = iqmp +        self._public_numbers = public_numbers + +    p = utils.read_only_property("_p") +    q = utils.read_only_property("_q") +    d = utils.read_only_property("_d") +    dmp1 = utils.read_only_property("_dmp1") +    dmq1 = utils.read_only_property("_dmq1") +    iqmp = utils.read_only_property("_iqmp") +    public_numbers = utils.read_only_property("_public_numbers") + +    def private_key(self, backend): +        return backend.load_rsa_private_numbers(self) + + +class RSAPublicNumbers(object): +    def __init__(self, e, n): +        if ( +            not isinstance(e, six.integer_types) or +            not isinstance(n, six.integer_types) +        ): +            raise TypeError("RSAPublicNumbers arguments must be integers.") + +        self._e = e +        self._n = n + +    e = utils.read_only_property("_e") +    n = utils.read_only_property("_n") + +    def public_key(self, backend): +        return backend.load_rsa_public_numbers(self) + +    def __repr__(self): +        return "<RSAPublicNumbers(e={0}, n={1})>".format(self._e, self._n) diff --git a/src/cryptography/hazmat/primitives/ciphers/__init__.py b/src/cryptography/hazmat/primitives/ciphers/__init__.py new file mode 100644 index 00000000..e5a8ca52 --- /dev/null +++ b/src/cryptography/hazmat/primitives/ciphers/__init__.py @@ -0,0 +1,21 @@ +# 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.hazmat.primitives.ciphers.base import Cipher + + +__all__ = [ +    "Cipher", +] diff --git a/src/cryptography/hazmat/primitives/ciphers/algorithms.py b/src/cryptography/hazmat/primitives/ciphers/algorithms.py new file mode 100644 index 00000000..bd8437c2 --- /dev/null +++ b/src/cryptography/hazmat/primitives/ciphers/algorithms.py @@ -0,0 +1,147 @@ +# 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 + + +def _verify_key_size(algorithm, key): +    # Verify that the key size matches the expected key size +    if len(key) * 8 not in algorithm.key_sizes: +        raise ValueError("Invalid key size ({0}) for {1}.".format( +            len(key) * 8, algorithm.name +        )) +    return key + + +@utils.register_interface(interfaces.BlockCipherAlgorithm) +@utils.register_interface(interfaces.CipherAlgorithm) +class AES(object): +    name = "AES" +    block_size = 128 +    key_sizes = frozenset([128, 192, 256]) + +    def __init__(self, key): +        self.key = _verify_key_size(self, key) + +    @property +    def key_size(self): +        return len(self.key) * 8 + + +@utils.register_interface(interfaces.BlockCipherAlgorithm) +@utils.register_interface(interfaces.CipherAlgorithm) +class Camellia(object): +    name = "camellia" +    block_size = 128 +    key_sizes = frozenset([128, 192, 256]) + +    def __init__(self, key): +        self.key = _verify_key_size(self, key) + +    @property +    def key_size(self): +        return len(self.key) * 8 + + +@utils.register_interface(interfaces.BlockCipherAlgorithm) +@utils.register_interface(interfaces.CipherAlgorithm) +class TripleDES(object): +    name = "3DES" +    block_size = 64 +    key_sizes = frozenset([64, 128, 192]) + +    def __init__(self, key): +        if len(key) == 8: +            key += key + key +        elif len(key) == 16: +            key += key[:8] +        self.key = _verify_key_size(self, key) + +    @property +    def key_size(self): +        return len(self.key) * 8 + + +@utils.register_interface(interfaces.BlockCipherAlgorithm) +@utils.register_interface(interfaces.CipherAlgorithm) +class Blowfish(object): +    name = "Blowfish" +    block_size = 64 +    key_sizes = frozenset(range(32, 449, 8)) + +    def __init__(self, key): +        self.key = _verify_key_size(self, key) + +    @property +    def key_size(self): +        return len(self.key) * 8 + + +@utils.register_interface(interfaces.BlockCipherAlgorithm) +@utils.register_interface(interfaces.CipherAlgorithm) +class CAST5(object): +    name = "CAST5" +    block_size = 64 +    key_sizes = frozenset(range(40, 129, 8)) + +    def __init__(self, key): +        self.key = _verify_key_size(self, key) + +    @property +    def key_size(self): +        return len(self.key) * 8 + + +@utils.register_interface(interfaces.CipherAlgorithm) +class ARC4(object): +    name = "RC4" +    key_sizes = frozenset([40, 56, 64, 80, 128, 192, 256]) + +    def __init__(self, key): +        self.key = _verify_key_size(self, key) + +    @property +    def key_size(self): +        return len(self.key) * 8 + + +@utils.register_interface(interfaces.CipherAlgorithm) +class IDEA(object): +    name = "IDEA" +    block_size = 64 +    key_sizes = frozenset([128]) + +    def __init__(self, key): +        self.key = _verify_key_size(self, key) + +    @property +    def key_size(self): +        return len(self.key) * 8 + + +@utils.register_interface(interfaces.BlockCipherAlgorithm) +@utils.register_interface(interfaces.CipherAlgorithm) +class SEED(object): +    name = "SEED" +    block_size = 128 +    key_sizes = frozenset([128]) + +    def __init__(self, key): +        self.key = _verify_key_size(self, key) + +    @property +    def key_size(self): +        return len(self.key) * 8 diff --git a/src/cryptography/hazmat/primitives/ciphers/base.py b/src/cryptography/hazmat/primitives/ciphers/base.py new file mode 100644 index 00000000..e3fe5adc --- /dev/null +++ b/src/cryptography/hazmat/primitives/ciphers/base.py @@ -0,0 +1,132 @@ +# 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.exceptions import ( +    AlreadyFinalized, AlreadyUpdated, NotYetFinalized, UnsupportedAlgorithm, +    _Reasons +) +from cryptography.hazmat.backends.interfaces import CipherBackend +from cryptography.hazmat.primitives import interfaces + + +class Cipher(object): +    def __init__(self, algorithm, mode, backend): +        if not isinstance(backend, CipherBackend): +            raise UnsupportedAlgorithm( +                "Backend object does not implement CipherBackend.", +                _Reasons.BACKEND_MISSING_INTERFACE +            ) + +        if not isinstance(algorithm, interfaces.CipherAlgorithm): +            raise TypeError( +                "Expected interface of interfaces.CipherAlgorithm." +            ) + +        if mode is not None: +            mode.validate_for_algorithm(algorithm) + +        self.algorithm = algorithm +        self.mode = mode +        self._backend = backend + +    def encryptor(self): +        if isinstance(self.mode, interfaces.ModeWithAuthenticationTag): +            if self.mode.tag is not None: +                raise ValueError( +                    "Authentication tag must be None when encrypting." +                ) +        ctx = self._backend.create_symmetric_encryption_ctx( +            self.algorithm, self.mode +        ) +        return self._wrap_ctx(ctx, encrypt=True) + +    def decryptor(self): +        if isinstance(self.mode, interfaces.ModeWithAuthenticationTag): +            if self.mode.tag is None: +                raise ValueError( +                    "Authentication tag must be provided when decrypting." +                ) +        ctx = self._backend.create_symmetric_decryption_ctx( +            self.algorithm, self.mode +        ) +        return self._wrap_ctx(ctx, encrypt=False) + +    def _wrap_ctx(self, ctx, encrypt): +        if isinstance(self.mode, interfaces.ModeWithAuthenticationTag): +            if encrypt: +                return _AEADEncryptionContext(ctx) +            else: +                return _AEADCipherContext(ctx) +        else: +            return _CipherContext(ctx) + + +@utils.register_interface(interfaces.CipherContext) +class _CipherContext(object): +    def __init__(self, ctx): +        self._ctx = ctx + +    def update(self, data): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        return self._ctx.update(data) + +    def finalize(self): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        data = self._ctx.finalize() +        self._ctx = None +        return data + + +@utils.register_interface(interfaces.AEADCipherContext) +@utils.register_interface(interfaces.CipherContext) +class _AEADCipherContext(object): +    def __init__(self, ctx): +        self._ctx = ctx +        self._tag = None +        self._updated = False + +    def update(self, data): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        self._updated = True +        return self._ctx.update(data) + +    def finalize(self): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        data = self._ctx.finalize() +        self._tag = self._ctx.tag +        self._ctx = None +        return data + +    def authenticate_additional_data(self, data): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        if self._updated: +            raise AlreadyUpdated("Update has been called on this context.") +        self._ctx.authenticate_additional_data(data) + + +@utils.register_interface(interfaces.AEADEncryptionContext) +class _AEADEncryptionContext(_AEADCipherContext): +    @property +    def tag(self): +        if self._ctx is not None: +            raise NotYetFinalized("You must finalize encryption before " +                                  "getting the tag.") +        return self._tag diff --git a/src/cryptography/hazmat/primitives/ciphers/modes.py b/src/cryptography/hazmat/primitives/ciphers/modes.py new file mode 100644 index 00000000..d995b876 --- /dev/null +++ b/src/cryptography/hazmat/primitives/ciphers/modes.py @@ -0,0 +1,125 @@ +# 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 + + +def _check_iv_length(self, algorithm): +    if len(self.initialization_vector) * 8 != algorithm.block_size: +        raise ValueError("Invalid IV size ({0}) for {1}.".format( +            len(self.initialization_vector), self.name +        )) + + +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithInitializationVector) +class CBC(object): +    name = "CBC" + +    def __init__(self, initialization_vector): +        self._initialization_vector = initialization_vector + +    initialization_vector = utils.read_only_property("_initialization_vector") +    validate_for_algorithm = _check_iv_length + + +@utils.register_interface(interfaces.Mode) +class ECB(object): +    name = "ECB" + +    def validate_for_algorithm(self, algorithm): +        pass + + +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithInitializationVector) +class OFB(object): +    name = "OFB" + +    def __init__(self, initialization_vector): +        self._initialization_vector = initialization_vector + +    initialization_vector = utils.read_only_property("_initialization_vector") +    validate_for_algorithm = _check_iv_length + + +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithInitializationVector) +class CFB(object): +    name = "CFB" + +    def __init__(self, initialization_vector): +        self._initialization_vector = initialization_vector + +    initialization_vector = utils.read_only_property("_initialization_vector") +    validate_for_algorithm = _check_iv_length + + +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithInitializationVector) +class CFB8(object): +    name = "CFB8" + +    def __init__(self, initialization_vector): +        self._initialization_vector = initialization_vector + +    initialization_vector = utils.read_only_property("_initialization_vector") +    validate_for_algorithm = _check_iv_length + + +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithNonce) +class CTR(object): +    name = "CTR" + +    def __init__(self, nonce): +        self._nonce = nonce + +    nonce = utils.read_only_property("_nonce") + +    def validate_for_algorithm(self, algorithm): +        if len(self.nonce) * 8 != algorithm.block_size: +            raise ValueError("Invalid nonce size ({0}) for {1}.".format( +                len(self.nonce), self.name +            )) + + +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithInitializationVector) +@utils.register_interface(interfaces.ModeWithAuthenticationTag) +class GCM(object): +    name = "GCM" + +    def __init__(self, initialization_vector, tag=None, min_tag_length=16): +        # len(initialization_vector) must in [1, 2 ** 64), but it's impossible +        # to actually construct a bytes object that large, so we don't check +        # for it +        if min_tag_length < 4: +            raise ValueError("min_tag_length must be >= 4") +        if tag is not None and len(tag) < min_tag_length: +            raise ValueError( +                "Authentication tag must be {0} bytes or longer.".format( +                    min_tag_length) +            ) + +        self._initialization_vector = initialization_vector +        self._tag = tag + +    tag = utils.read_only_property("_tag") +    initialization_vector = utils.read_only_property("_initialization_vector") + +    def validate_for_algorithm(self, algorithm): +        pass diff --git a/src/cryptography/hazmat/primitives/cmac.py b/src/cryptography/hazmat/primitives/cmac.py new file mode 100644 index 00000000..6f722031 --- /dev/null +++ b/src/cryptography/hazmat/primitives/cmac.py @@ -0,0 +1,75 @@ +# 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.exceptions import ( +    AlreadyFinalized, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.interfaces import CMACBackend +from cryptography.hazmat.primitives import interfaces + + +@utils.register_interface(interfaces.MACContext) +class CMAC(object): +    def __init__(self, algorithm, backend, ctx=None): +        if not isinstance(backend, CMACBackend): +            raise UnsupportedAlgorithm( +                "Backend object does not implement CMACBackend.", +                _Reasons.BACKEND_MISSING_INTERFACE +            ) + +        if not isinstance(algorithm, interfaces.BlockCipherAlgorithm): +            raise TypeError( +                "Expected instance of interfaces.BlockCipherAlgorithm." +            ) +        self._algorithm = algorithm + +        self._backend = backend +        if ctx is None: +            self._ctx = self._backend.create_cmac_ctx(self._algorithm) +        else: +            self._ctx = ctx + +    def update(self, data): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        if not isinstance(data, bytes): +            raise TypeError("data must be bytes.") +        self._ctx.update(data) + +    def finalize(self): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        digest = self._ctx.finalize() +        self._ctx = None +        return digest + +    def verify(self, signature): +        if not isinstance(signature, bytes): +            raise TypeError("signature must be bytes.") +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") + +        ctx, self._ctx = self._ctx, None +        ctx.verify(signature) + +    def copy(self): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        return CMAC( +            self._algorithm, +            backend=self._backend, +            ctx=self._ctx.copy() +        ) diff --git a/src/cryptography/hazmat/primitives/constant_time.py b/src/cryptography/hazmat/primitives/constant_time.py new file mode 100644 index 00000000..a14eda85 --- /dev/null +++ b/src/cryptography/hazmat/primitives/constant_time.py @@ -0,0 +1,47 @@ +# 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 hmac +import os + +from cryptography.hazmat.bindings.utils import LazyLibrary, build_ffi + + +with open(os.path.join(os.path.dirname(__file__), "src/constant_time.h")) as f: +    TYPES = f.read() + +with open(os.path.join(os.path.dirname(__file__), "src/constant_time.c")) as f: +    FUNCTIONS = f.read() + + +_ffi = build_ffi(cdef_source=TYPES, verify_source=FUNCTIONS) +_lib = LazyLibrary(_ffi) + + +if hasattr(hmac, "compare_digest"): +    def bytes_eq(a, b): +        if not isinstance(a, bytes) or not isinstance(b, bytes): +            raise TypeError("a and b must be bytes.") + +        return hmac.compare_digest(a, b) + +else: +    def bytes_eq(a, b): +        if not isinstance(a, bytes) or not isinstance(b, bytes): +            raise TypeError("a and b must be bytes.") + +        return _lib.Cryptography_constant_time_bytes_eq( +            a, len(a), b, len(b) +        ) == 1 diff --git a/src/cryptography/hazmat/primitives/hashes.py b/src/cryptography/hazmat/primitives/hashes.py new file mode 100644 index 00000000..8c2284e3 --- /dev/null +++ b/src/cryptography/hazmat/primitives/hashes.py @@ -0,0 +1,121 @@ +# 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.exceptions import ( +    AlreadyFinalized, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.interfaces import HashBackend +from cryptography.hazmat.primitives import interfaces + + +@utils.register_interface(interfaces.HashContext) +class Hash(object): +    def __init__(self, algorithm, backend, ctx=None): +        if not isinstance(backend, HashBackend): +            raise UnsupportedAlgorithm( +                "Backend object does not implement HashBackend.", +                _Reasons.BACKEND_MISSING_INTERFACE +            ) + +        if not isinstance(algorithm, interfaces.HashAlgorithm): +            raise TypeError("Expected instance of interfaces.HashAlgorithm.") +        self._algorithm = algorithm + +        self._backend = backend + +        if ctx is None: +            self._ctx = self._backend.create_hash_ctx(self.algorithm) +        else: +            self._ctx = ctx + +    algorithm = utils.read_only_property("_algorithm") + +    def update(self, data): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        if not isinstance(data, bytes): +            raise TypeError("data must be bytes.") +        self._ctx.update(data) + +    def copy(self): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        return Hash( +            self.algorithm, backend=self._backend, ctx=self._ctx.copy() +        ) + +    def finalize(self): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        digest = self._ctx.finalize() +        self._ctx = None +        return digest + + +@utils.register_interface(interfaces.HashAlgorithm) +class SHA1(object): +    name = "sha1" +    digest_size = 20 +    block_size = 64 + + +@utils.register_interface(interfaces.HashAlgorithm) +class SHA224(object): +    name = "sha224" +    digest_size = 28 +    block_size = 64 + + +@utils.register_interface(interfaces.HashAlgorithm) +class SHA256(object): +    name = "sha256" +    digest_size = 32 +    block_size = 64 + + +@utils.register_interface(interfaces.HashAlgorithm) +class SHA384(object): +    name = "sha384" +    digest_size = 48 +    block_size = 128 + + +@utils.register_interface(interfaces.HashAlgorithm) +class SHA512(object): +    name = "sha512" +    digest_size = 64 +    block_size = 128 + + +@utils.register_interface(interfaces.HashAlgorithm) +class RIPEMD160(object): +    name = "ripemd160" +    digest_size = 20 +    block_size = 64 + + +@utils.register_interface(interfaces.HashAlgorithm) +class Whirlpool(object): +    name = "whirlpool" +    digest_size = 64 +    block_size = 64 + + +@utils.register_interface(interfaces.HashAlgorithm) +class MD5(object): +    name = "md5" +    digest_size = 16 +    block_size = 64 diff --git a/src/cryptography/hazmat/primitives/hmac.py b/src/cryptography/hazmat/primitives/hmac.py new file mode 100644 index 00000000..47a048ff --- /dev/null +++ b/src/cryptography/hazmat/primitives/hmac.py @@ -0,0 +1,78 @@ +# 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.exceptions import ( +    AlreadyFinalized, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.interfaces import HMACBackend +from cryptography.hazmat.primitives import interfaces + + +@utils.register_interface(interfaces.MACContext) +@utils.register_interface(interfaces.HashContext) +class HMAC(object): +    def __init__(self, key, algorithm, backend, ctx=None): +        if not isinstance(backend, HMACBackend): +            raise UnsupportedAlgorithm( +                "Backend object does not implement HMACBackend.", +                _Reasons.BACKEND_MISSING_INTERFACE +            ) + +        if not isinstance(algorithm, interfaces.HashAlgorithm): +            raise TypeError("Expected instance of interfaces.HashAlgorithm.") +        self._algorithm = algorithm + +        self._backend = backend +        self._key = key +        if ctx is None: +            self._ctx = self._backend.create_hmac_ctx(key, self.algorithm) +        else: +            self._ctx = ctx + +    algorithm = utils.read_only_property("_algorithm") + +    def update(self, data): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        if not isinstance(data, bytes): +            raise TypeError("data must be bytes.") +        self._ctx.update(data) + +    def copy(self): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        return HMAC( +            self._key, +            self.algorithm, +            backend=self._backend, +            ctx=self._ctx.copy() +        ) + +    def finalize(self): +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") +        digest = self._ctx.finalize() +        self._ctx = None +        return digest + +    def verify(self, signature): +        if not isinstance(signature, bytes): +            raise TypeError("signature must be bytes.") +        if self._ctx is None: +            raise AlreadyFinalized("Context was already finalized.") + +        ctx, self._ctx = self._ctx, None +        ctx.verify(signature) diff --git a/src/cryptography/hazmat/primitives/interfaces.py b/src/cryptography/hazmat/primitives/interfaces.py new file mode 100644 index 00000000..370fd68a --- /dev/null +++ b/src/cryptography/hazmat/primitives/interfaces.py @@ -0,0 +1,499 @@ +# 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 abc + +import six + + +@six.add_metaclass(abc.ABCMeta) +class CipherAlgorithm(object): +    @abc.abstractproperty +    def name(self): +        """ +        A string naming this mode (e.g. "AES", "Camellia"). +        """ + +    @abc.abstractproperty +    def key_size(self): +        """ +        The size of the key being used as an integer in bits (e.g. 128, 256). +        """ + + +@six.add_metaclass(abc.ABCMeta) +class BlockCipherAlgorithm(object): +    @abc.abstractproperty +    def block_size(self): +        """ +        The size of a block as an integer in bits (e.g. 64, 128). +        """ + + +@six.add_metaclass(abc.ABCMeta) +class Mode(object): +    @abc.abstractproperty +    def name(self): +        """ +        A string naming this mode (e.g. "ECB", "CBC"). +        """ + +    @abc.abstractmethod +    def validate_for_algorithm(self, algorithm): +        """ +        Checks that all the necessary invariants of this (mode, algorithm) +        combination are met. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class ModeWithInitializationVector(object): +    @abc.abstractproperty +    def initialization_vector(self): +        """ +        The value of the initialization vector for this mode as bytes. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class ModeWithNonce(object): +    @abc.abstractproperty +    def nonce(self): +        """ +        The value of the nonce for this mode as bytes. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class ModeWithAuthenticationTag(object): +    @abc.abstractproperty +    def tag(self): +        """ +        The value of the tag supplied to the constructor of this mode. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class CipherContext(object): +    @abc.abstractmethod +    def update(self, data): +        """ +        Processes the provided bytes through the cipher and returns the results +        as bytes. +        """ + +    @abc.abstractmethod +    def finalize(self): +        """ +        Returns the results of processing the final block as bytes. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class AEADCipherContext(object): +    @abc.abstractmethod +    def authenticate_additional_data(self, data): +        """ +        Authenticates the provided bytes. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class AEADEncryptionContext(object): +    @abc.abstractproperty +    def tag(self): +        """ +        Returns tag bytes. This is only available after encryption is +        finalized. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class PaddingContext(object): +    @abc.abstractmethod +    def update(self, data): +        """ +        Pads the provided bytes and returns any available data as bytes. +        """ + +    @abc.abstractmethod +    def finalize(self): +        """ +        Finalize the padding, returns bytes. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class HashAlgorithm(object): +    @abc.abstractproperty +    def name(self): +        """ +        A string naming this algorithm (e.g. "sha256", "md5"). +        """ + +    @abc.abstractproperty +    def digest_size(self): +        """ +        The size of the resulting digest in bytes. +        """ + +    @abc.abstractproperty +    def block_size(self): +        """ +        The internal block size of the hash algorithm in bytes. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class HashContext(object): +    @abc.abstractproperty +    def algorithm(self): +        """ +        A HashAlgorithm that will be used by this context. +        """ + +    @abc.abstractmethod +    def update(self, data): +        """ +        Processes the provided bytes through the hash. +        """ + +    @abc.abstractmethod +    def finalize(self): +        """ +        Finalizes the hash context and returns the hash digest as bytes. +        """ + +    @abc.abstractmethod +    def copy(self): +        """ +        Return a HashContext that is a copy of the current context. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class RSAPrivateKey(object): +    @abc.abstractmethod +    def signer(self, padding, algorithm): +        """ +        Returns an AsymmetricSignatureContext used for signing data. +        """ + +    @abc.abstractmethod +    def decrypt(self, ciphertext, padding): +        """ +        Decrypts the provided ciphertext. +        """ + +    @abc.abstractproperty +    def key_size(self): +        """ +        The bit length of the public modulus. +        """ + +    @abc.abstractmethod +    def public_key(self): +        """ +        The RSAPublicKey associated with this private key. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class RSAPrivateKeyWithNumbers(RSAPrivateKey): +    @abc.abstractmethod +    def private_numbers(self): +        """ +        Returns an RSAPrivateNumbers. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class RSAPublicKey(object): +    @abc.abstractmethod +    def verifier(self, signature, padding, algorithm): +        """ +        Returns an AsymmetricVerificationContext used for verifying signatures. +        """ + +    @abc.abstractmethod +    def encrypt(self, plaintext, padding): +        """ +        Encrypts the given plaintext. +        """ + +    @abc.abstractproperty +    def key_size(self): +        """ +        The bit length of the public modulus. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class RSAPublicKeyWithNumbers(RSAPublicKey): +    @abc.abstractmethod +    def public_numbers(self): +        """ +        Returns an RSAPublicNumbers +        """ + + +@six.add_metaclass(abc.ABCMeta) +class DSAParameters(object): +    @abc.abstractmethod +    def generate_private_key(self): +        """ +        Generates and returns a DSAPrivateKey. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class DSAParametersWithNumbers(DSAParameters): +    @abc.abstractmethod +    def parameter_numbers(self): +        """ +        Returns a DSAParameterNumbers. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class DSAPrivateKey(object): +    @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.abstractmethod +    def parameters(self): +        """ +        The DSAParameters object associated with this private key. +        """ + +    @abc.abstractmethod +    def signer(self, signature_algorithm): +        """ +        Returns an AsymmetricSignatureContext used for signing data. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class DSAPrivateKeyWithNumbers(DSAPrivateKey): +    @abc.abstractmethod +    def private_numbers(self): +        """ +        Returns a DSAPrivateNumbers. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class DSAPublicKey(object): +    @abc.abstractproperty +    def key_size(self): +        """ +        The bit length of the prime modulus. +        """ + +    @abc.abstractmethod +    def parameters(self): +        """ +        The DSAParameters object associated with this public key. +        """ + +    @abc.abstractmethod +    def verifier(self, signature, signature_algorithm): +        """ +        Returns an AsymmetricVerificationContext used for signing data. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class DSAPublicKeyWithNumbers(DSAPublicKey): +    @abc.abstractmethod +    def public_numbers(self): +        """ +        Returns a DSAPublicNumbers. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class AsymmetricSignatureContext(object): +    @abc.abstractmethod +    def update(self, data): +        """ +        Processes the provided bytes and returns nothing. +        """ + +    @abc.abstractmethod +    def finalize(self): +        """ +        Returns the signature as bytes. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class AsymmetricVerificationContext(object): +    @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. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class AsymmetricPadding(object): +    @abc.abstractproperty +    def name(self): +        """ +        A string naming this padding (e.g. "PSS", "PKCS1"). +        """ + + +@six.add_metaclass(abc.ABCMeta) +class KeyDerivationFunction(object): +    @abc.abstractmethod +    def derive(self, key_material): +        """ +        Deterministically generates and returns a new key based on the existing +        key material. +        """ + +    @abc.abstractmethod +    def verify(self, key_material, expected_key): +        """ +        Checks whether the key generated by the key material matches the +        expected derived key. Raises an exception if they do not match. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class EllipticCurve(object): +    @abc.abstractproperty +    def name(self): +        """ +        The name of the curve. e.g. secp256r1. +        """ + +    @abc.abstractproperty +    def key_size(self): +        """ +        The bit length of the base point of the curve. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class EllipticCurveSignatureAlgorithm(object): +    @abc.abstractproperty +    def algorithm(self): +        """ +        The digest algorithm used with this signature. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class EllipticCurvePrivateKey(object): +    @abc.abstractmethod +    def signer(self, signature_algorithm): +        """ +        Returns an AsymmetricSignatureContext used for signing data. +        """ + +    @abc.abstractmethod +    def public_key(self): +        """ +        The EllipticCurvePublicKey for this private key. +        """ + +    @abc.abstractproperty +    def curve(self): +        """ +        The EllipticCurve that this key is on. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class EllipticCurvePrivateKeyWithNumbers(EllipticCurvePrivateKey): +    @abc.abstractmethod +    def private_numbers(self): +        """ +        Returns an EllipticCurvePrivateNumbers. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class EllipticCurvePublicKey(object): +    @abc.abstractmethod +    def verifier(self, signature, signature_algorithm): +        """ +        Returns an AsymmetricVerificationContext used for signing data. +        """ + +    @abc.abstractproperty +    def curve(self): +        """ +        The EllipticCurve that this key is on. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class EllipticCurvePublicKeyWithNumbers(EllipticCurvePublicKey): +    @abc.abstractmethod +    def public_numbers(self): +        """ +        Returns an EllipticCurvePublicNumbers. +        """ + + +@six.add_metaclass(abc.ABCMeta) +class MACContext(object): +    @abc.abstractmethod +    def update(self, data): +        """ +        Processes the provided bytes. +        """ + +    @abc.abstractmethod +    def finalize(self): +        """ +        Returns the message authentication code as bytes. +        """ + +    @abc.abstractmethod +    def copy(self): +        """ +        Return a MACContext that is a copy of the current context. +        """ + +    @abc.abstractmethod +    def verify(self, signature): +        """ +        Checks if the generated message authentication code matches the +        signature. +        """ + +# DeprecatedIn07 +CMACContext = MACContext diff --git a/src/cryptography/hazmat/primitives/kdf/__init__.py b/src/cryptography/hazmat/primitives/kdf/__init__.py new file mode 100644 index 00000000..2f420574 --- /dev/null +++ b/src/cryptography/hazmat/primitives/kdf/__init__.py @@ -0,0 +1,14 @@ +# 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 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 diff --git a/src/cryptography/hazmat/primitives/kdf/pbkdf2.py b/src/cryptography/hazmat/primitives/kdf/pbkdf2.py new file mode 100644 index 00000000..97b6408c --- /dev/null +++ b/src/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 + +from cryptography import utils +from cryptography.exceptions import ( +    AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.interfaces import PBKDF2HMACBackend +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 isinstance(backend, PBKDF2HMACBackend): +            raise UnsupportedAlgorithm( +                "Backend object does not implement PBKDF2HMACBackend.", +                _Reasons.BACKEND_MISSING_INTERFACE +            ) + +        if not backend.pbkdf2_hmac_supported(algorithm): +            raise UnsupportedAlgorithm( +                "{0} is not supported for PBKDF2 by this backend.".format( +                    algorithm.name), +                _Reasons.UNSUPPORTED_HASH +            ) +        self._used = False +        self._algorithm = algorithm +        self._length = length +        if not isinstance(salt, bytes): +            raise TypeError("salt must be bytes.") +        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 not isinstance(key_material, bytes): +            raise TypeError("key_material must be bytes.") +        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.") diff --git a/src/cryptography/hazmat/primitives/padding.py b/src/cryptography/hazmat/primitives/padding.py new file mode 100644 index 00000000..7aeeff7c --- /dev/null +++ b/src/cryptography/hazmat/primitives/padding.py @@ -0,0 +1,164 @@ +# 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 +from cryptography.hazmat.bindings.utils import LazyLibrary, build_ffi +from cryptography.hazmat.primitives import interfaces + + +TYPES = """ +uint8_t Cryptography_check_pkcs7_padding(const uint8_t *, uint8_t); +""" + +FUNCTIONS = """ +/* Returns the value of the input with the most-significant-bit copied to all +   of the bits. */ +static uint8_t Cryptography_DUPLICATE_MSB_TO_ALL(uint8_t a) { +    return (1 - (a >> (sizeof(uint8_t) * 8 - 1))) - 1; +} + +/* This returns 0xFF if a < b else 0x00, but does so in a constant time +   fashion */ +static uint8_t Cryptography_constant_time_lt(uint8_t a, uint8_t b) { +    a -= b; +    return Cryptography_DUPLICATE_MSB_TO_ALL(a); +} + +uint8_t Cryptography_check_pkcs7_padding(const uint8_t *data, +                                         uint8_t block_len) { +    uint8_t i; +    uint8_t pad_size = data[block_len - 1]; +    uint8_t mismatch = 0; +    for (i = 0; i < block_len; i++) { +        unsigned int mask = Cryptography_constant_time_lt(i, pad_size); +        uint8_t b = data[block_len - 1 - i]; +        mismatch |= (mask & (pad_size ^ b)); +    } + +    /* Check to make sure the pad_size was within the valid range. */ +    mismatch |= ~Cryptography_constant_time_lt(0, pad_size); +    mismatch |= Cryptography_constant_time_lt(block_len, pad_size); + +    /* Make sure any bits set are copied to the lowest bit */ +    mismatch |= mismatch >> 4; +    mismatch |= mismatch >> 2; +    mismatch |= mismatch >> 1; +    /* Now check the low bit to see if it's set */ +    return (mismatch & 1) == 0; +} +""" + + +_ffi = build_ffi(cdef_source=TYPES, verify_source=FUNCTIONS) +_lib = LazyLibrary(_ffi) + + +class PKCS7(object): +    def __init__(self, block_size): +        if not (0 <= block_size < 256): +            raise ValueError("block_size must be in range(0, 256).") + +        if block_size % 8 != 0: +            raise ValueError("block_size must be a multiple of 8.") + +        self.block_size = block_size + +    def padder(self): +        return _PKCS7PaddingContext(self.block_size) + +    def unpadder(self): +        return _PKCS7UnpaddingContext(self.block_size) + + +@utils.register_interface(interfaces.PaddingContext) +class _PKCS7PaddingContext(object): +    def __init__(self, block_size): +        self.block_size = block_size +        # TODO: more copies than necessary, we should use zero-buffer (#193) +        self._buffer = b"" + +    def update(self, data): +        if self._buffer is None: +            raise AlreadyFinalized("Context was already finalized.") + +        if not isinstance(data, bytes): +            raise TypeError("data must be bytes.") + +        self._buffer += data + +        finished_blocks = len(self._buffer) // (self.block_size // 8) + +        result = self._buffer[:finished_blocks * (self.block_size // 8)] +        self._buffer = self._buffer[finished_blocks * (self.block_size // 8):] + +        return result + +    def finalize(self): +        if self._buffer is None: +            raise AlreadyFinalized("Context was already finalized.") + +        pad_size = self.block_size // 8 - len(self._buffer) +        result = self._buffer + six.int2byte(pad_size) * pad_size +        self._buffer = None +        return result + + +@utils.register_interface(interfaces.PaddingContext) +class _PKCS7UnpaddingContext(object): +    def __init__(self, block_size): +        self.block_size = block_size +        # TODO: more copies than necessary, we should use zero-buffer (#193) +        self._buffer = b"" + +    def update(self, data): +        if self._buffer is None: +            raise AlreadyFinalized("Context was already finalized.") + +        if not isinstance(data, bytes): +            raise TypeError("data must be bytes.") + +        self._buffer += data + +        finished_blocks = max( +            len(self._buffer) // (self.block_size // 8) - 1, +            0 +        ) + +        result = self._buffer[:finished_blocks * (self.block_size // 8)] +        self._buffer = self._buffer[finished_blocks * (self.block_size // 8):] + +        return result + +    def finalize(self): +        if self._buffer is None: +            raise AlreadyFinalized("Context was already finalized.") + +        if len(self._buffer) != self.block_size // 8: +            raise ValueError("Invalid padding bytes.") + +        valid = _lib.Cryptography_check_pkcs7_padding( +            self._buffer, self.block_size // 8 +        ) + +        if not valid: +            raise ValueError("Invalid padding bytes.") + +        pad_size = six.indexbytes(self._buffer, -1) +        res = self._buffer[:-pad_size] +        self._buffer = None +        return res diff --git a/src/cryptography/hazmat/primitives/serialization.py b/src/cryptography/hazmat/primitives/serialization.py new file mode 100644 index 00000000..0fb560e0 --- /dev/null +++ b/src/cryptography/hazmat/primitives/serialization.py @@ -0,0 +1,50 @@ +# 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 warnings + +from cryptography import utils + + +def load_pem_traditional_openssl_private_key(data, password, backend): +    warnings.warn( +        "load_pem_traditional_openssl_private_key is deprecated and will be " +        "removed in a future version, use load_pem_private_key instead.", +        utils.DeprecatedIn06, +        stacklevel=2 +    ) + +    return backend.load_traditional_openssl_pem_private_key( +        data, password +    ) + + +def load_pem_pkcs8_private_key(data, password, backend): +    warnings.warn( +        "load_pem_pkcs8_private_key is deprecated and will be removed in a " +        "future version, use load_pem_private_key instead.", +        utils.DeprecatedIn06, +        stacklevel=2 +    ) + +    return backend.load_pkcs8_pem_private_key(data, password) + + +def load_pem_private_key(data, password, backend): +    return backend.load_pem_private_key(data, password) + + +def load_pem_public_key(data, backend): +    return backend.load_pem_public_key(data) diff --git a/src/cryptography/hazmat/primitives/src/constant_time.c b/src/cryptography/hazmat/primitives/src/constant_time.c new file mode 100644 index 00000000..13ac4ab9 --- /dev/null +++ b/src/cryptography/hazmat/primitives/src/constant_time.c @@ -0,0 +1,31 @@ +// 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. + +uint8_t Cryptography_constant_time_bytes_eq(uint8_t *a, size_t len_a, +                                            uint8_t *b, size_t len_b) { +    size_t i = 0; +    uint8_t mismatch = 0; +    if (len_a != len_b) { +        return 0; +    } +    for (i = 0; i < len_a; i++) { +        mismatch |= a[i] ^ b[i]; +    } + +    /* Make sure any bits set are copied to the lowest bit */ +    mismatch |= mismatch >> 4; +    mismatch |= mismatch >> 2; +    mismatch |= mismatch >> 1; +    /* Now check the low bit to see if it's set */ +    return (mismatch & 1) == 0; +} diff --git a/src/cryptography/hazmat/primitives/src/constant_time.h b/src/cryptography/hazmat/primitives/src/constant_time.h new file mode 100644 index 00000000..4f41034e --- /dev/null +++ b/src/cryptography/hazmat/primitives/src/constant_time.h @@ -0,0 +1,16 @@ +// 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. + + +uint8_t Cryptography_constant_time_bytes_eq(uint8_t *, size_t, uint8_t *, +                                            size_t); diff --git a/src/cryptography/hazmat/primitives/twofactor/__init__.py b/src/cryptography/hazmat/primitives/twofactor/__init__.py new file mode 100644 index 00000000..2f420574 --- /dev/null +++ b/src/cryptography/hazmat/primitives/twofactor/__init__.py @@ -0,0 +1,14 @@ +# 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 diff --git a/src/cryptography/hazmat/primitives/twofactor/hotp.py b/src/cryptography/hazmat/primitives/twofactor/hotp.py new file mode 100644 index 00000000..d0b476a7 --- /dev/null +++ b/src/cryptography/hazmat/primitives/twofactor/hotp.py @@ -0,0 +1,69 @@ +# 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, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.interfaces import HMACBackend +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 not isinstance(backend, HMACBackend): +            raise UnsupportedAlgorithm( +                "Backend object does not implement HMACBackend.", +                _Reasons.BACKEND_MISSING_INTERFACE +            ) + +        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 = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111 +        p = hmac_value[offset:offset + 4] +        return struct.unpack(">I", p)[0] & 0x7fffffff diff --git a/src/cryptography/hazmat/primitives/twofactor/totp.py b/src/cryptography/hazmat/primitives/twofactor/totp.py new file mode 100644 index 00000000..854c5163 --- /dev/null +++ b/src/cryptography/hazmat/primitives/twofactor/totp.py @@ -0,0 +1,41 @@ +# 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, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.interfaces import HMACBackend +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): +        if not isinstance(backend, HMACBackend): +            raise UnsupportedAlgorithm( +                "Backend object does not implement HMACBackend.", +                _Reasons.BACKEND_MISSING_INTERFACE +            ) + +        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/src/cryptography/utils.py b/src/cryptography/utils.py new file mode 100644 index 00000000..03c8c0e8 --- /dev/null +++ b/src/cryptography/utils.py @@ -0,0 +1,64 @@ +# 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 abc +import inspect +import sys + + +DeprecatedIn06 = DeprecationWarning + + +def register_interface(iface): +    def register_decorator(klass): +        verify_interface(iface, klass) +        iface.register(klass) +        return klass +    return register_decorator + + +def read_only_property(name): +    return property(lambda self: getattr(self, name)) + + +class InterfaceNotImplemented(Exception): +    pass + + +def verify_interface(iface, klass): +    for method in iface.__abstractmethods__: +        if not hasattr(klass, method): +            raise InterfaceNotImplemented( +                "{0} is missing a {1!r} method".format(klass, method) +            ) +        if isinstance(getattr(iface, method), abc.abstractproperty): +            # Can't properly verify these yet. +            continue +        spec = inspect.getargspec(getattr(iface, method)) +        actual = inspect.getargspec(getattr(klass, method)) +        if spec != actual: +            raise InterfaceNotImplemented( +                "{0}.{1}'s signature differs from the expected. Expected: " +                "{2!r}. Received: {3!r}".format( +                    klass, method, spec, actual +                ) +            ) + + +def bit_length(x): +    if sys.version_info >= (2, 7): +        return x.bit_length() +    else: +        return len(bin(x)) - (2 + (x <= 0))  | 
