def _get_finished_message(self, function): """ Helper to implement :py:meth:`get_finished` and :py:meth:`get_peer_finished`. :param function: Either :py:data:`SSL_get_finished`: or :py:data:`SSL_get_peer_finished`. :return: :py:data:`None` if the desired message has not yet been received, otherwise the contents of the message. :rtype: :py:class:`bytes` or :py:class:`NoneType` """ # The OpenSSL documentation says nothing about what might happen if the # count argument given is zero. Specifically, it doesn't say whether # the output buffer may be NULL in that case or not. Inspection of the # implementation reveals that it calls memcpy() unconditionally. # Section 7.1.4, paragraph 1 of the C standard suggests that # memcpy(NULL, source, 0) is not guaranteed to produce defined (let # alone desirable) behavior (though it probably does on just about # every implementation...) # # Allocate a tiny buffer to pass in (instead of just passing NULL as # one might expect) for the initial call so as to be safe against this # potentially undefined behavior. empty = _ffi.new("char[]", 0) size = function(self._ssl, empty, 0) if size == 0: # No Finished message so far. return None buf = _ffi.new("char[]", size) function(self._ssl, buf, size) return _ffi.buffer(buf, size)[:]
def master_key(self): """ Get a copy of the master key. :return: A string representing the state """ if self._ssl.session == _ffi.NULL: return None return _ffi.buffer(self._ssl.session.master_key, self._ssl.session.master_key_length)[:]
def client_random(self): """ Get a copy of the client hello nonce. :return: A string representing the state """ if self._ssl.session == _ffi.NULL: return None return _ffi.buffer(self._ssl.s3.client_random, _lib.SSL3_RANDOM_SIZE)[:]
def master_key(self): """ Get a copy of the master key. :return: A string representing the state """ if self._ssl.session == _ffi.NULL: return None return _ffi.buffer( self._ssl.session.master_key, self._ssl.session.master_key_length)[:]
def client_random(self): """ Get a copy of the client hello nonce. :return: A string representing the state """ if self._ssl.session == _ffi.NULL: return None return _ffi.buffer( self._ssl.s3.client_random, _lib.SSL3_RANDOM_SIZE)[:]
def recv(self, bufsiz, flags=None): """ Receive data on the connection. NOTE: If you get one of the WantRead, WantWrite or WantX509Lookup exceptions on this, you have to call the method again with the SAME buffer. :param bufsiz: The maximum number of bytes to read :param flags: (optional) Included for compatibility with the socket API, the value is ignored :return: The string read from the Connection """ buf = _ffi.new("char[]", bufsiz) result = _lib.SSL_read(self._ssl, buf, bufsiz) self._raise_ssl_error(self._ssl, result) return _ffi.buffer(buf, result)[:]
def bio_read(self, bufsiz): """ When using non-socket connections this function reads the "dirty" data that would have traveled away on the network. :param bufsiz: The maximum number of bytes to read :return: The string read. """ if self._from_ssl is None: raise TypeError("Connection sock was not None") if not isinstance(bufsiz, integer_types): raise TypeError("bufsiz must be an integer") buf = _ffi.new("char[]", bufsiz) result = _lib.BIO_read(self._from_ssl, buf, bufsiz) if result <= 0: self._handle_bio_errors(self._from_ssl, result) return _ffi.buffer(buf, result)[:]
def bytes(num_bytes): """ Get some random bytes as a string. :param num_bytes: The number of bytes to fetch :return: A string of random bytes """ if not isinstance(num_bytes, _integer_types): raise TypeError("num_bytes must be an integer") if num_bytes < 0: raise ValueError("num_bytes must not be negative") result_buffer = _ffi.new("char[]", num_bytes) result_code = _lib.RAND_bytes(result_buffer, num_bytes) if result_code == -1: # TODO: No tests for this code path. Triggering a RAND_bytes failure # might involve supplying a custom ENGINE? That's hard. _raise_current_error() return _ffi.buffer(result_buffer)[:]