Skip to content

Commit

Permalink
Support stateless session resumption via session tickets
Browse files Browse the repository at this point in the history
  • Loading branch information
jtackaberry committed Jan 26, 2012
1 parent e113782 commit da590e1
Showing 1 changed file with 50 additions and 4 deletions.
54 changes: 50 additions & 4 deletions src/net/tls/openssl.py
Expand Up @@ -40,6 +40,8 @@
import logging
import os
import binascii
import hmac
import hashlib
from fnmatch import fnmatch # for wildcard certs
from datetime import datetime
from ctypes import *
Expand Down Expand Up @@ -85,6 +87,7 @@
SSL_CTRL_MODE = 33
SSL_CTRL_SET_SESS_CACHE_SIZE = 42
SSL_CTRL_SET_SESS_CACHE_MODE = 44
SSL_CTRL_SET_TLSEXT_TICKET_KEYS = 59

SSL_SESS_CACHE_OFF = 0x0000
SSL_SESS_CACHE_CLIENT = 0x0001
Expand Down Expand Up @@ -867,6 +870,7 @@ def __init__(self, protocol=None):
self._libssl = libssl
# For _passwd_cb()
self._key_passwd = None
self._ticket_key = None
self._local_cert = None
self._verify_location = None
self._dh_size = None
Expand Down Expand Up @@ -1142,6 +1146,41 @@ def set_ciphers(self, ciphers):
_check(libssl.SSL_CTX_set_cipher_list(self._ctx, ciphers))


@property
def ticket_key(self):
"""
A 48 byte string used to encrypt session tickets.
Setting this value enables the session ticket extension, and clients
will receive session tickets during handshake. Clients that present
tickets encrypted using this key can resume SSL sessions without the
need for server-side session cache.
Clients don't need to do anything special to use the tickets except
to set :attr:`~TLSSocket.reuse_sessions`.
.. note::
The actual key used to encrypt the tickets is derived from an
HMAC of the local certificate using the given key. This means it
is necessary to first call :meth:`load_cert_chain`.
"""
return self._ticket_key


@ticket_key.setter
def ticket_key(self, key):
# Follow Apache's approach to generate the actual ticket key: use
# an HMAC of the server certificate with the user-supplied secret.
if len(key) < 48:
raise ValueError('ticket key must be at least 48 bytes long')
elif not self._local_cert:
raise ValueError('must call load_cert_chain() first')
self._ticket_key = key
digest = self._local_cert.digest('sha384')
private = hmac.new(key, digest, hashlib.sha384).digest()
_check(libssl.SSL_CTX_ctrl(self._ctx, SSL_CTRL_SET_TLSEXT_TICKET_KEYS, 48, private))


@property
def dh_size(self):
"""
Expand Down Expand Up @@ -1822,8 +1861,9 @@ def reuse_sessions(self):
True if session state will be preserved between connections.
SSL session resumption performs an abbreviated handshake if the server
side recognizes the session id. This is allows substantially more
efficient reconnections.
side recognizes the session id, or if the client presents a valid
session ticket the server can decrypt. This is allows substantially
more efficient reconnections.
This value is False by default. If True, for clients, the session
state will be perserved between subsequent :meth:`connect` and
Expand Down Expand Up @@ -1932,6 +1972,9 @@ def _starttls(self, **kwargs):
# certs it is impossible.
raise TLSError('CA bundle not found but verification requested')

if 'ticket_key' in kwargs and ctx.ticket_key != kwargs['ticket_key']:
ctx.ticket_key = kwargs['ticket_key']

self._membio = _SSLMemoryBIO(ctx)
if kwargs['client']:
libssl.SSL_set_connect_state(self._membio.ssl)
Expand Down Expand Up @@ -1986,7 +2029,7 @@ def starttls_client(self, cert=None, key=None, password=None, verify=True, cn=No
cn=cn, fingerprint=fingerprint)


def starttls_server(self, cert=None, key=None, password=None, verify=False, dh=None):
def starttls_server(self, cert=None, key=None, password=None, verify=False, dh=None, ticket_key=None):
"""
Wait for a ClientHello before continuing communication.
Expand All @@ -1999,11 +2042,14 @@ def starttls_server(self, cert=None, key=None, password=None, verify=False, dh=N
:param dh: filename for Diffie-Hellman parameters in PEM format, needed
for EDH ciphers. If None, temporary DH params will be used.
:type dh: str
:param ticket_key: corresponds to :attr:`TLSContext.ticket_key`
:type ticket_key: str
:returns: :class:`~kaa.InProgress` that finishes when TLS handshaking
succeeds or fails. If the handshake fails, an exception is
thrown to the InProgress
Before this method (or :meth:`~TLSSocket.starttls_client`) is invoked,
a TLSSocket behaves like a standard :class:`~kaa.Socket`.
"""
return self._starttls(client=False, cert=cert, key=key, password=password, verify=verify, dh=dh)
return self._starttls(client=False, cert=cert, key=key, password=password,
verify=verify, dh=dh, ticket_key=ticket_key)

0 comments on commit da590e1

Please sign in to comment.