Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions awscrt/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,10 @@ class TlsCipherPref(IntEnum):
PQ_DEFAULT = 8 # :
"""Recommended default policy with post-quantum algorithm support. This policy may change over time."""

TLSv1_2_2025_07 = 9
"""A TLS Cipher Preference ordering that supports TLS 1.2 through TLS 1.3, and does not include CBC cipher suites.
It is FIPS-complaint."""
Copy link
Contributor

@sfod sfod Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debatable: While the does not include CBC cipher suites part is true, and that's what we originally requested from s2n, there are more differences. See aws/s2n-tls#5375

When I added the policy to c-io, I used abstract tightened security, but now thinking more about it, maybe we should give details on what this preference provides, something like the following:

A TLS cipher preference requiring TLS 1.2+ with FIPS compliance and perfect forward secrecy. Supports AES-GCM and ECDHE cipher suites with ECDSA and RSA-PSS signature schemes. Uses NIST P-256 and P-384 curves only.


def is_supported(self):
"""Return whether this Cipher Preference is available in the underlying platform's TLS implementation"""
return _awscrt.is_tls_cipher_supported(self.value)
Expand Down
37 changes: 37 additions & 0 deletions test/test_mqtt5.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from awscrt import mqtt5, io, http, exceptions
from test import test_retry_wrapper, NativeResourceTest
import os
import sys
import unittest
import uuid
import time
Expand Down Expand Up @@ -288,6 +289,42 @@ def _test_direct_connect_mutual_tls(self):
def test_direct_connect_mutual_tls(self):
test_retry_wrapper(self._test_direct_connect_mutual_tls)

def test_direct_connect_mutual_tls_with_tlsv1_2_2025(self):
test_retry_wrapper(self._test_direct_connect_mutual_tls_with_tlsv1_2_2025)

def _test_direct_connect_mutual_tls_with_tlsv1_2_2025(self):
input_host_name = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_HOST")
input_cert = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_RSA_CERT")
input_key = _get_env_variable("AWS_TEST_MQTT5_IOT_CORE_RSA_KEY")

client_options = mqtt5.ClientOptions(
host_name=input_host_name,
port=8883
)
tls_ctx_options = io.TlsContextOptions.create_client_with_mtls_from_path(
input_cert,
input_key
)

try:
tls_ctx_options.cipher_pref = io.TlsCipherPref.TLSv1_2_2025_07
client_options.tls_ctx = io.ClientTlsContext(tls_ctx_options)
except Exception as e:
if sys.platform.startswith("linux"):
# On Linux, this should not fail
self.fail(f"Unexpected error on Linux: {e}")
else:
# On non-Linux platforms, verify we get the expected error and skip
self.assertIn('AWS_IO_TLS_CIPHER_PREF_UNSUPPORTED', str(e))
self.skipTest(f"TLSv1_2_2025_07 not supported on {sys.platform}")

callbacks = Mqtt5TestCallbacks()
client = self._create_client(client_options=client_options, callbacks=callbacks)
client.start()
callbacks.future_connection_success.result(TIMEOUT)
client.stop()
callbacks.future_stopped.result(TIMEOUT)

def _test_direct_connect_http_proxy_tls(self):
input_host_name = _get_env_variable("AWS_TEST_MQTT5_DIRECT_MQTT_TLS_HOST")
input_port = int(_get_env_variable("AWS_TEST_MQTT5_DIRECT_MQTT_TLS_PORT"))
Expand Down