Skip to content

Commit

Permalink
[#488] Fix --resum when server only supports TLS 1.3
Browse files Browse the repository at this point in the history
  • Loading branch information
nabla-c0d3 committed Feb 7, 2021
1 parent 26f6ffa commit b65026f
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 80 deletions.
4 changes: 3 additions & 1 deletion docs/available-scan-commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,13 @@ Result class
Session Resumption Support
**************************

**ScanCommand.SESSION_RESUMPTION**: Test a server for session resumption support using session IDs and TLS tickets.
**ScanCommand.SESSION_RESUMPTION**: Test a server for TLS 1.2 session resumption support using session IDs and TLS tickets.

Result class
============

.. autoclass:: TlsSessionIdSupportEnum
.. autoclass:: TlsSessionTicketSupportEnum
.. autoclass:: SessionResumptionSupportScanResult

Session Resumption Rate
Expand Down
2 changes: 2 additions & 0 deletions sslyze/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
from sslyze.plugins.session_resumption.implementation import (
SessionResumptionSupportScanResult,
SessionResumptionRateScanResult,
TlsSessionTicketSupportEnum,
TlsSessionIdSupportEnum,
)
from sslyze.plugins.compression_plugin import CompressionScanResult
from sslyze.plugins.early_data_plugin import EarlyDataScanResult
Expand Down
57 changes: 48 additions & 9 deletions sslyze/plugins/session_resumption/_resumption_with_id.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,58 @@
from enum import Enum
from enum import Enum, unique
from typing import Optional, Tuple

import nassl

from sslyze.errors import ServerRejectedTlsHandshake
from sslyze.server_connectivity import ServerConnectivityInfo, TlsVersionEnum


@unique
class TlsSessionIdSupportEnum(Enum):
"""The result of attempting to resume TLS sessions with the server using Session IDs.
Attributes:
FULLY_SUPPORTED: All the session resumption attempts were successful.
PARTIALLY_SUPPORTED: Only some of the session resumption attempts were successful.
NOT_SUPPORTED: None of the session resumption attempts were successful.
SERVER_IS_TLS_1_3_ONLY: The server only supports TLS 1.3 which does not support Session IDs resumption.
"""

FULLY_SUPPORTED = 1
PARTIALLY_SUPPORTED = 2
NOT_SUPPORTED = 3
SERVER_IS_TLS_1_3_ONLY = 4


class _ScanJobResultEnum(Enum):
TLS_TICKET_RESUMPTION = 1
SESSION_ID_RESUMPTION = 2


class ServerOnlySupportsTls13(Exception):
"""If the server only supports TLS 1.3 or higher, it does not support session resumption with IDs or tickets.
"""
pass


def resume_tls_session(
server_info: ServerConnectivityInfo,
tls_version_to_use: TlsVersionEnum,
tls_session: Optional[nassl._nassl.SSL_SESSION] = None,
should_enable_tls_ticket: bool = False,
) -> nassl._nassl.SSL_SESSION:
"""Connect to the server and returns the session object that was assigned for that connection.
If ssl_session is given, tries to resume that session.
"""
# Try with TLS 1.2 even if the server supports TLS 1.3 or higher as there is no session resumption (with IDs or
# tickets) with TLS 1.3
if server_info.tls_probing_result.highest_tls_version_supported.value >= TlsVersionEnum.TLS_1_3.value:
tls_version_to_use = TlsVersionEnum.TLS_1_2
downgraded_from_tls_1_3 = True
else:
tls_version_to_use = server_info.tls_probing_result.highest_tls_version_supported
downgraded_from_tls_1_3 = False

ssl_connection = server_info.get_preconfigured_tls_connection(override_tls_version=tls_version_to_use)
if not should_enable_tls_ticket:
# Need to disable TLS tickets to test session IDs, according to rfc5077:
Expand All @@ -31,9 +64,16 @@ def resume_tls_session(
ssl_connection.ssl_client.set_session(tls_session)

try:
# Perform the SSL handshake
# Perform the TLS handshake
ssl_connection.connect()
new_session = ssl_connection.ssl_client.get_session() # Get session data

except ServerRejectedTlsHandshake:
if downgraded_from_tls_1_3:
raise ServerOnlySupportsTls13()
else:
raise

finally:
ssl_connection.close()

Expand All @@ -48,12 +88,11 @@ def _extract_session_id(ssl_session: nassl._nassl.SSL_SESSION) -> str:
return session_id


def resume_with_session_id(
server_info: ServerConnectivityInfo, tls_version_to_use: TlsVersionEnum
) -> Tuple[_ScanJobResultEnum, bool]:
def resume_with_session_id(server_info: ServerConnectivityInfo) -> Tuple[_ScanJobResultEnum, bool]:
"""Perform one session resumption using Session IDs.
"""
session1 = resume_tls_session(server_info, tls_version_to_use)
# Create a new TLS session with the server
session1 = resume_tls_session(server_info)
try:
# Recover the session ID
session1_id = _extract_session_id(session1)
Expand All @@ -65,8 +104,8 @@ def resume_with_session_id(
# Session ID empty
return _ScanJobResultEnum.SESSION_ID_RESUMPTION, False

# Try to resume that SSL session
session2 = resume_tls_session(server_info, tls_version_to_use, session1)
# Try to resume that TLS session
session2 = resume_tls_session(server_info, tls_session=session1)
try:
# Recover the session ID
session2_id = _extract_session_id(session2)
Expand Down
39 changes: 20 additions & 19 deletions sslyze/plugins/session_resumption/_resumption_with_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,52 @@
import nassl

from sslyze.plugins.session_resumption._resumption_with_id import resume_tls_session, _ScanJobResultEnum
from sslyze.server_connectivity import ServerConnectivityInfo, TlsVersionEnum
from sslyze.errors import ServerRejectedTlsHandshake
from sslyze.server_connectivity import ServerConnectivityInfo


@unique
class TslSessionTicketSupportEnum(Enum):
class TlsSessionTicketSupportEnum(Enum):
"""The result of attempting to resume a TLS session with the server using TLS Tickets.
"""

SUCCEEDED = 1
FAILED_TICKET_NOT_ASSIGNED = 2
FAILED_TICKED_IGNORED = 3
FAILED_ONLY_TLS_1_3_SUPPORTED = 4

# TODO(AD): Switch to these names for v5.0.0 and leverage ServerOnlySupportsTls13() to simplify flow
# SUPPORTED = 1
# NOT_SUPPORTED_TICKET_NOT_ASSIGNED = 2
# NOT_SUPPORTED_TICKET_IGNORED = 3
# SERVER_IS_TLS_1_3_ONLY = 4


def resume_with_tls_ticket(
server_info: ServerConnectivityInfo, tls_version_to_use: TlsVersionEnum
) -> Tuple[_ScanJobResultEnum, TslSessionTicketSupportEnum]:
server_info: ServerConnectivityInfo,
) -> Tuple[_ScanJobResultEnum, TlsSessionTicketSupportEnum]:
"""Perform one session resumption using TLS Session Tickets.
"""
# Connect to the server and keep the SSL session
try:
session1 = resume_tls_session(server_info, tls_version_to_use, should_enable_tls_ticket=True)
except ServerRejectedTlsHandshake:
if server_info.tls_probing_result.highest_tls_version_supported.value >= TlsVersionEnum.TLS_1_3.value:
return _ScanJobResultEnum.TLS_TICKET_RESUMPTION, TslSessionTicketSupportEnum.FAILED_ONLY_TLS_1_3_SUPPORTED
else:
raise

# Connect to the server and keep the TLS session
session1 = resume_tls_session(server_info, should_enable_tls_ticket=True)
try:
# Recover the TLS ticket
session1_tls_ticket = _extract_tls_session_ticket(session1)
except IndexError:
return _ScanJobResultEnum.TLS_TICKET_RESUMPTION, TslSessionTicketSupportEnum.FAILED_TICKET_NOT_ASSIGNED
return _ScanJobResultEnum.TLS_TICKET_RESUMPTION, TlsSessionTicketSupportEnum.FAILED_TICKET_NOT_ASSIGNED

# Try to resume that session using the TLS ticket
session2 = resume_tls_session(server_info, tls_version_to_use, session1, should_enable_tls_ticket=True)
session2 = resume_tls_session(server_info, tls_session=session1, should_enable_tls_ticket=True)
try:
# Recover the TLS ticket
session2_tls_ticket = _extract_tls_session_ticket(session2)
except IndexError:
return _ScanJobResultEnum.TLS_TICKET_RESUMPTION, TslSessionTicketSupportEnum.FAILED_TICKET_NOT_ASSIGNED
return _ScanJobResultEnum.TLS_TICKET_RESUMPTION, TlsSessionTicketSupportEnum.FAILED_TICKET_NOT_ASSIGNED

# Finally, compare the two TLS Tickets
if session1_tls_ticket != session2_tls_ticket:
return _ScanJobResultEnum.TLS_TICKET_RESUMPTION, TslSessionTicketSupportEnum.FAILED_TICKED_IGNORED
return _ScanJobResultEnum.TLS_TICKET_RESUMPTION, TlsSessionTicketSupportEnum.FAILED_TICKED_IGNORED

return _ScanJobResultEnum.TLS_TICKET_RESUMPTION, TslSessionTicketSupportEnum.SUCCEEDED
return _ScanJobResultEnum.TLS_TICKET_RESUMPTION, TlsSessionTicketSupportEnum.SUCCEEDED


def _extract_tls_session_ticket(ssl_session: nassl._nassl.SSL_SESSION) -> str:
Expand Down
Loading

0 comments on commit b65026f

Please sign in to comment.