예제 #1
0
    def validate_against_certs(self, certs):
        """
        Given a CFArray of trust roots, validate against them.

        Returns True if the certificate is valid, otherwise returns False.
        """
        trust = ffi.new("SecTrustRef *")
        certs = ffi.cast("CFArrayRef", certs)
        status = lib.SSLCopyPeerTrust(self._ctx, trust)
        _raise_on_error(status)

        if trust[0] == ffi.NULL:
            raise TLSError("Unable to allocate memory!")

        try:
            status = lib.SecTrustSetAnchorCertificates(trust[0], certs)
            _raise_on_error(status)

            status = lib.SecTrustSetAnchorCertificatesOnly(trust[0], True)
            _raise_on_error(status)

            result = ffi.new("SecTrustResultType *")
            status = lib.SecTrustEvaluate(trust[0], result)
            _raise_on_error(status)
        finally:
            lib.CFRelease(trust[0])

        # Ok, we know what is going on now. Check the result.
        successes = (lib.kSecTrustResultUnspecified,
                     lib.kSecTrustResultProceed)
        if result[0] in successes:
            return True

        return False
예제 #2
0
    def read(self, size):
        """
        Performs a normal application-level read operation.

        :param size: The maximum number of bytes to read.
        :type size: ``int``

        :returns: The read bytes.
        :rtype: ``bytes``
        """
        buffer = ffi.new("char[]", size)
        read_count = ffi.new("size_t *")

        status = lib.SSLRead(self._ctx, buffer, size, read_count)

        # We need to catch a weird behaviour here: Apple allows a call to
        # SSLRead to return errSSLWouldBlock but also to have read some data.
        # For our case, we want to consider that as a non-error condition.
        # Short writes are just fine by us.
        read_bytes = read_count[0]
        short_read = (status == lib.errSSLWouldBlock and read_bytes)
        if not short_read:
            _raise_on_error(status)

        return ffi.buffer(buffer, read_bytes)[:]
예제 #3
0
    def get_peer_domain_name(self):
        """
        Retrieves the peer domain name specified previously.
        """
        length = ffi.new("size_t *")
        status = lib.SSLGetPeerDomainNameLength(self._ctx, length)
        _raise_on_error(status)

        name = ffi.new("char[]", length[0])
        status = lib.SSLGetPeerDomainName(self._ctx, name, length)
        _raise_on_error(status)

        return name[:length[0]]
예제 #4
0
    def get_peer_id(self):
        """
        Retrieves the current peer ID data.

        If the peer ID data for this context was not set by calling the
        set_peer_id function, this function returns None. Otherwise, returns
        the data as opaque bytes.

        :returns: The peer ID binary data, or ``None``.
        """
        peer_id_data = ffi.new("void **")
        peer_id_len = ffi.new("size_t *")
        status = lib.SSLGetPeerID(self._ctx, peer_id_data, peer_id_len)
        _raise_on_error(status)
        return ffi.buffer(peer_id_data[0], peer_id_len[0])[:]
예제 #5
0
    def get_enabled_ciphers(self):
        """
        Determines which SSL cipher suites are currently enabled.

        :returns: A list of supported ciphers, as enum members from
            ``SSLCipherSuites``.
        :rtype: ``list`` of ``SSLCipherSuites``.
        """
        cipher_count = ffi.new("size_t *")
        status = lib.SSLGetNumberEnabledCiphers(self._ctx, cipher_count)
        _raise_on_error(status)

        ciphers = ffi.new("SSLCipherSuite[]", cipher_count[0])
        status = lib.SSLGetEnabledCiphers(self._ctx, ciphers, cipher_count)
        _raise_on_error(status)
        return ciphers[:]
예제 #6
0
    def get_supported_ciphers(self):
        """
        Determines the values of the supported cipher suites.

        :returns: A list of supported ciphers, as enum members from
            ``SSLCipherSuites``.
        :rtype: ``list`` of ``SSLCipherSuites``.
        """
        cipher_count = ffi.new("size_t *")
        status = lib.SSLGetNumberSupportedCiphers(self._ctx, cipher_count)
        _raise_on_error(status)

        ciphers = ffi.new("SSLCipherSuite[]", cipher_count[0])
        status = lib.SSLGetSupportedCiphers(self._ctx, ciphers, cipher_count)
        _raise_on_error(status)
        return ciphers[:]
예제 #7
0
 def get_session_option(self, option):
     """
     Gets the current value of an SSL session option.
     """
     value = ffi.new("Boolean *")
     status = lib.SSLGetSessionOption(self._ctx, option, value)
     _raise_on_error(status)
     return bool(value[0])
예제 #8
0
    def get_diffie_hellman_params(self):
        """
        Retrieves the Diffie-Hellman parameters specified earlier.

        This function returns the parameter block specified in an earlier call
        to the function set_diffie_hellman_params. If set_diffie_hellman_params
        was never called, this function returns None.
        """
        dh_params = ffi.new("void **")
        dh_params_len = ffi.new("size_t *")

        status = lib.SSLGetDiffieHellmanParams(self._ctx, dh_params,
                                               dh_params_len)
        _raise_on_error(status)

        if dh_params[0] == ffi.NULL:
            return None

        return dh_params[0][:dh_params_len[0]]
예제 #9
0
    def get_session_state(self):
        """
        Retrieves the state of an SSL session.

        :returns: A session state enum value.
        """
        state = ffi.new("SSLSessionState *")
        status = lib.SSLGetSessionState(self._ctx, state)
        _raise_on_error(status)
        return SSLSessionState(state[0])
예제 #10
0
    def get_buffered_read_size(self):
        """
        Determines how much data is available to be read.

        This function determines how much data you can be guaranteed to obtain
        in a call to the read function. This function does not block or cause
        any low-level read operations to occur.
        """
        buffer_size = ffi.new("size_t *")
        status = lib.SSLGetBufferedReadSize(self._ctx, buffer_size)
        _raise_on_error(status)
        return buffer_size[0]
예제 #11
0
    def get_negotiated_cipher(self):
        """
        Retrieves the cipher suite negotiated for this session.

        You should call this function only when a session is active.

        :returns: The negotiated cipher.
        :rtype: Member of ``SSLCipherSuites``.
        """
        cipher = ffi.new("SSLCipherSuite *")
        status = lib.SSLGetNegotiatedCipher(self._ctx, cipher)
        _raise_on_error(status)

        return cipher[0]
예제 #12
0
    def get_negotiated_protocol_version(self):
        """
        Obtains the negotiated protocol version of the active session.

        This function retrieves the version of SSL or TLS protocol negotiated
        for the session. Note that the negotiated protocol may not be the same
        as your preferred protocol, depending on which protocol versions you
        enabled with the set_protocol_version_enabled function. This function
        can return any of the following values:

        - kSSLProtocol2
        - kSSLProtocol3
        - kTLSProtocol1
        - kSSLProtocolUnknown
        """
        version = ffi.new("SSLProtocol *")
        status = lib.SSLGetNegotiatedProtocolVersion(self._ctx, version)
        _raise_on_error(status)
        return SSLProtocol(version[0])
예제 #13
0
    def set_enabled_ciphers(self, ciphers):
        """
        Specifies a restricted set of SSL cipher suites to be enabled by the
        current SSL session context.

        You can call this function, for example, to limit cipher suites to
        those that use exportable key sizes or to those supported by a
        particular protocol version.

        This function can be called only when no session is active. The default
        set of enabled cipher suites is the complete set of supported cipher
        suites obtained by calling the get_supported_ciphers function.

        Call the get_enabled_ciphers function to determine which SSL cipher
        suites are currently enabled.

        :param ciphers: A list of enum members from ``SSLCipherSuites``
            representing the ciphers to enable.
        :type ciphers: ``list`` of ``SSLCipherSuites``
        """
        ffi_ciphers = ffi.new("SSLCipherSuite[]", ciphers)
        status = lib.SSLSetEnabledCiphers(self._ctx, ffi_ciphers, len(ciphers))
        _raise_on_error(status)
예제 #14
0
    def write(self, data):
        """
        Performs a normal application-level write operation.

        :returns: The number of bytes written.
        :rtype: ``int``
        """
        if isinstance(data, memoryview):
            data = data.tobytes()

        write_count = ffi.new("size_t *")
        status = lib.SSLWrite(self._ctx, data, len(data), write_count)

        # We need to catch a weird behaviour here: Apple allows a call to
        # SSLWrite to return errSSLWouldBlock but also to have written some
        # data. For our case, we want to consider that as a non-error
        # condition. Short writes are just fine by us.
        written_bytes = write_count[0]
        short_write = (status == lib.errSSLWouldBlock and written_bytes)

        if not short_write:
            _raise_on_error(status)

        return written_bytes