diff --git a/wrapper/python/wolfssl/Makefile b/wrapper/python/wolfssl/Makefile index f2ac26192..ec5288e50 100644 --- a/wrapper/python/wolfssl/Makefile +++ b/wrapper/python/wolfssl/Makefile @@ -18,7 +18,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -.PHONY : all clean clean-build clean-pyc clean-test install test docs upload +.PHONY : all clean clean-build clean-pyc clean-test clean-docs install test docs upload # builds the module all : @@ -29,7 +29,7 @@ install : all python ./setup.py install ## removes all build, test, coverage and Python artifacts -clean : clean-test clean-build clean-pyc +clean : clean-test clean-build clean-pyc clean-docs ## removes test and coverage artifacts clean-test : @@ -47,6 +47,10 @@ clean-pyc : find src test -name '*.pyc' -exec rm -f {} + find src test -name '*.pyo' -exec rm -f {} + +## removes documentation file artifacts +clean-docs : + $(MAKE) -C docs clean + # runs unit tests check : test @@ -54,7 +58,7 @@ test : clean-pyc tox docs : - $(MAKE) -C docs singlehtml + $(MAKE) -C docs html # publishes module at pypi upload : test diff --git a/wrapper/python/wolfssl/README.rst b/wrapper/python/wolfssl/README.rst index 1a8e7250a..f55ee06bb 100644 --- a/wrapper/python/wolfssl/README.rst +++ b/wrapper/python/wolfssl/README.rst @@ -6,12 +6,12 @@ resource-constrained environments primarily because of its small size, speed, and portability. Installation -============ +------------ In order to use ``wolfssl Python``, you'll also need to install ``wolfssl C``. Mac OSX -------- +~~~~~~~ Installing from ``homebrew`` and ``pip`` package managers: @@ -41,7 +41,7 @@ Installing from ``source code``: Linux ------ +~~~~~ .. code-block:: shell @@ -65,7 +65,7 @@ Linux Testing -======= +------- To run the tox tests in the source code, you'll need ``tox`` and a few other requirements. The source code relies at **WOLFSSL_DIR/wrapper/python/wolfssl** diff --git a/wrapper/python/wolfssl/docs/api.rst b/wrapper/python/wolfssl/docs/api.rst index 04beebb85..e6d2538ae 100644 --- a/wrapper/python/wolfssl/docs/api.rst +++ b/wrapper/python/wolfssl/docs/api.rst @@ -1,23 +1,21 @@ -API -=== +API Documentation +================= .. module:: wolfssl +wrap_socket +----------- + +.. autofunction:: wrap_socket + SSL/TLS Context --------------- -SSLContext -~~~~~~~~~~ - .. autoclass:: SSLContext :members: SSL/TLS Socket -------------- - -SSLSocket -~~~~~~~~~ - .. autoclass:: SSLSocket :members: diff --git a/wrapper/python/wolfssl/docs/examples.rst b/wrapper/python/wolfssl/docs/examples.rst new file mode 100644 index 000000000..5d2279f62 --- /dev/null +++ b/wrapper/python/wolfssl/docs/examples.rst @@ -0,0 +1,95 @@ +Client and Server Examples +========================== + +SSL/TLS Client Example +---------------------- + +.. code-block:: python + + import socket + import wolfssl + + CA_DATA = \ + """ + -----BEGIN CERTIFICATE----- + MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs + MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 + d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j + ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL + MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 + LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug + RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm + +9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW + PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM + xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB + Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 + hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg + EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF + MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA + FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec + nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z + eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF + hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 + Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe + vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep + +OkuE6N36B9K + -----END CERTIFICATE----- + """ + + bind_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + + context = wolfssl.SSLContext(wolfssl.PROTOCOL_TLSv1_2) + + context.verify_mode = wolfssl.CERT_REQUIRED + context.load_verify_locations(cadata=CA_DATA) + + secure_socket = context.wrap_socket(bind_socket) + + secure_socket.connect(("www.python.org", 443)) + + secure_socket.write(b"GET / HTTP/1.1\n\n") + + print(secure_socket.read()) + + secure_socket.close() + + +SSL/TLS Server Example +---------------------- + +.. code-block:: python + + import socket + import wolfssl + + bind_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + + bind_socket.bind(("", 4433)) + bind_socket.listen(5) + + context = wolfssl.SSLContext(wolfssl.PROTOCOL_TLSv1_2, server_side=True) + + context.load_cert_chain("certs/server-cert.pem", "certs/server-key.pem") + + while True: + try: + secure_socket = None + + new_socket, from_addr = bind_socket.accept() + + secure_socket = context.wrap_socket(new_socket) + + print("Connection received from", from_addr) + + print("\n", secure_socket.read(), "\n") + secure_socket.write(b"I hear you fa shizzle!") + + except KeyboardInterrupt: + print() + break + + finally: + if secure_socket: + secure_socket.close() + + bind_socket.close() diff --git a/wrapper/python/wolfssl/docs/index.rst b/wrapper/python/wolfssl/docs/index.rst index 366d37f47..41df3bca8 100644 --- a/wrapper/python/wolfssl/docs/index.rst +++ b/wrapper/python/wolfssl/docs/index.rst @@ -1,15 +1,13 @@ .. include:: ../README.rst Summary -------- +======= .. toctree:: - :maxdepth: 1 + :maxdepth: 2 - context - socket - -.. automodule:: wolfssl - :members: + usage + api + examples .. include:: ../LICENSING.rst diff --git a/wrapper/python/wolfssl/docs/installation.rst b/wrapper/python/wolfssl/docs/installation.rst deleted file mode 100644 index 6b2b3ec68..000000000 --- a/wrapper/python/wolfssl/docs/installation.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../README.rst \ No newline at end of file diff --git a/wrapper/python/wolfssl/docs/licensing.rst b/wrapper/python/wolfssl/docs/licensing.rst deleted file mode 100644 index f5cc633bb..000000000 --- a/wrapper/python/wolfssl/docs/licensing.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../LICENSING.rst diff --git a/wrapper/python/wolfssl/docs/usage.rst b/wrapper/python/wolfssl/docs/usage.rst index c6029a7fe..47c2f0981 100644 --- a/wrapper/python/wolfssl/docs/usage.rst +++ b/wrapper/python/wolfssl/docs/usage.rst @@ -1,8 +1,87 @@ -Usage -===== +Basic Usage +=========== -SSL/TLS Client --------------- +The SSL/TLS protocol works securing an underlying TCP connection, this module +adds the secure layer around the Python standard library +`socket `_ module. -SSL/TLS Server --------------- +There are three different paths to secure a socket in this module: + +* Using the top level function wolfssl.wrap_socket(); +* Using the method wrap_socket() from a SSLContext instance; +* Creating an SSLSocket object from the scratch. + +Note 1: + It is possible to use the same SSLContext for multiple SSLSockets to save + time and resources. + +Note 2: + Each path provides its own options for fine-tuning the securint parameters. + Check them out in the API documentation. + + +Using the top level function wolfssl.wrap_socket() +-------------------------------------------------- + +.. code-block:: python + + >>> import socket + >>> import wolfssl + >>> + >>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + >>> + >>> secure_socket = wolfssl.wrap_socket(sock) + >>> + >>> secure_socket.connect(("www.python.org", 443)) + >>> + >>> secure_socket.write(b"GET / HTTP/1.1\n\n") + >>> + >>> print(secure_socket.read()) + b'HTTP/1.1 500 Domain Not Found\r\nServer: Varnish\r\nRetry-After: 0\r\ncontent-type: text/html\r\nCache-Control: private, no-cache\r\nconnection: keep-alive\r\nContent-Length: 179\r\nAccept-Ranges: bytes\r\nDate: Sun, 05 Feb 2017 21:26:48 GMT\r\nVia: 1.1 varnish\r\nConnection: keep-alive\r\n\r\n\n\n\nFastly error: unknown domain \n\n\nFastly error: unknown domain: . Please check that this domain has been added to a service.' + >>> + >>> secure_socket.close() + + +Using the method wrap_socket() from a SSLContext instance +--------------------------------------------------------- + +.. code-block:: python + + >>> import socket + >>> import wolfssl + >>> + >>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + >>> + >>> context = wolfssl.SSLContext(wolfssl.PROTOCOL_TLSv1_2) + >>> + >>> secure_socket = context.wrap_socket(sock) + >>> + >>> secure_socket.connect(("www.python.org", 443)) + >>> + >>> secure_socket.write(b"GET / HTTP/1.1\n\n") + >>> + >>> print(secure_socket.read()) + b'HTTP/1.1 500 Domain Not Found\r\nServer: Varnish\r\nRetry-After: 0\r\ncontent-type: text/html\r\nCache-Control: private, no-cache\r\nconnection: keep-alive\r\nContent-Length: 179\r\nAccept-Ranges: bytes\r\nDate: Sun, 05 Feb 2017 21:26:48 GMT\r\nVia: 1.1 varnish\r\nConnection: keep-alive\r\n\r\n\n\n\nFastly error: unknown domain \n\n\nFastly error: unknown domain: . Please check that this domain has been added to a service.' + >>> + >>> secure_socket.close() + +Creating an SSLSocket object from the scratch +--------------------------------------------- + +.. code-block:: python + + >>> import socket + >>> import wolfssl + >>> + >>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + >>> + >>> secure_socket = wolfssl.SSLSocket(sock) + >>> + >>> secure_socket.connect(("www.python.org", 443)) + >>> + >>> secure_socket.write(b"GET / HTTP/1.1\n\n") + >>> + >>> print(secure_socket.read()) + b'HTTP/1.1 500 Domain Not Found\r\nServer: Varnish\r\nRetry-After: 0\r\ncontent-type: text/html\r\nCache-Control: private, no-cache\r\nconnection: keep-alive\r\nContent-Length: 179\r\nAccept-Ranges: bytes\r\nDate: Sun, 05 Feb 2017 21:26:48 GMT\r\nVia: 1.1 varnish\r\nConnection: keep-alive\r\n\r\n\n\n\nFastly error: unknown domain \n\n\nFastly error: unknown domain: . Please check that this domain has been added to a service.' + >>> + >>> secure_socket.close() diff --git a/wrapper/python/wolfssl/src/wolfssl/__init__.py b/wrapper/python/wolfssl/src/wolfssl/__init__.py index 27d081ca2..6d79fb025 100644 --- a/wrapper/python/wolfssl/src/wolfssl/__init__.py +++ b/wrapper/python/wolfssl/src/wolfssl/__init__.py @@ -187,6 +187,8 @@ class SSLContext(object): The keyfile string, if present, must point to a file containing the private key in. + + The password parameter is not supported yet. """ if password is not None: @@ -453,7 +455,7 @@ class SSLSocket(socket): raise ValueError("buffer not allowed in calls to " "read() on %s" % self.__class__) - data = t2b("\0" * length) + data = _ffi.new('byte[%d]' % length) length = _lib.wolfSSL_read(self.native_object, data, length) if length < 0: @@ -463,7 +465,7 @@ class SSLSocket(socket): else: raise SSLError("wolfSSL_read error (%d)" % err) - return data[:length] if length > 0 else b'' + return _ffi.buffer(data, length)[:] if length > 0 else b'' def recv(self, length=1024, flags=0): @@ -602,19 +604,13 @@ def wrap_socket(sock, keyfile=None, certfile=None, server_side=False, ciphers=None): """ Takes an instance sock of socket.socket, and returns an instance of - wolfssl.SSLSocket, a subtype of socket.socket, which wraps the underlying - socket in an SSL context. sock must be a SOCK_STREAM socket; other socket - types are unsupported. + wolfssl.SSLSocket, wraping the underlying socket in an SSL context. - For client-side sockets, the context construction is lazy; if the underlying - socket isn’t connected yet, the context construction will be performed after - connect() is called on the socket. For server-side sockets, if the socket - has no remote peer, it is assumed to be a listening socket, and the - server-side SSL wrapping is automatically performed on client connections - accepted via the accept() method. wrap_socket() may raise SSLError. + The sock parameter must be a SOCK_STREAM socket; other socket types are + unsupported. - The keyfile and certfile parameters specify optional files which contain a - certificate to be used to identify the local side of the connection. + The keyfile and certfile parameters specify optional files whith proper + key and the certificates used to identify the local side of the connection. The parameter server_side is a boolean which identifies whether server-side or client-side behavior is desired from this socket. @@ -622,9 +618,10 @@ def wrap_socket(sock, keyfile=None, certfile=None, server_side=False, The parameter cert_reqs specifies whether a certificate is required from the other side of the connection, and whether it will be validated if provided. It must be one of the three values: - CERT_NONE (certificates ignored) - CERT_OPTIONAL (not required, but validated if provided) - CERT_REQUIRED (required and validated) + + * CERT_NONE (certificates ignored) + * CERT_OPTIONAL (not required, but validated if provided) + * CERT_REQUIRED (required and validated) If the value of this parameter is not CERT_NONE, then the ca_certs parameter must point to a file of CA certificates. @@ -642,12 +639,19 @@ def wrap_socket(sock, keyfile=None, certfile=None, server_side=False, Here’s a table showing which versions in a client (down the side) can connect to which versions in a server (along the top): - | client \\ server | SSLv3 | TLS | TLSv1 | TLSv1.1 | TLSv1.2 | + +------------------+-------+-----+-------+---------+---------+ + | client \\ server | SSLv3 | TLS | TLSv1 | TLSv1.1 | TLSv1.2 | + +------------------+-------+-----+-------+---------+---------+ | SSLv3 | yes | yes | no | no | no | + +------------------+-------+-----+-------+---------+---------+ | TLS (SSLv23) | yes | yes | yes | yes | yes | + +------------------+-------+-----+-------+---------+---------+ | TLSv1 | no | yes | yes | no | no | + +------------------+-------+-----+-------+---------+---------+ | TLSv1.1 | no | yes | no | yes | no | + +------------------+-------+-----+-------+---------+---------+ | TLSv1.2 | no | yes | no | no | yes | + +------------------+-------+-----+-------+---------+---------+ Note: Which connections succeed will vary depending on the versions of the ssl @@ -663,11 +667,7 @@ def wrap_socket(sock, keyfile=None, certfile=None, server_side=False, gives the program control over the blocking behavior of the socket I/O involved in the handshake. - The parameter suppress_ragged_eofs specifies how the SSLSocket.recv() method - should signal unexpected EOF from the other end of the connection. If - specified as True (the default), it returns a normal EOF (an empty bytes - object) in response to unexpected EOF errors raised from the underlying - socket; if False, it will raise the exceptions back to the caller. + The parameter suppress_ragged_eofs is not supported yet. """ return SSLSocket(sock=sock, keyfile=keyfile, certfile=certfile, server_side=server_side, cert_reqs=cert_reqs,