Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/ci-al4.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
git config --global --add safe.directory /__w/CCF/CCF
mkdir build
cd build
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DTEST_HYBRID_TLS_GROUPS=ON ..
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug ..
ninja
shell: bash

Expand Down
22 changes: 5 additions & 17 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,6 @@ option(LONG_TESTS "Enable long end-to-end tests" OFF)
option(USE_SNMALLOC "Link against snmalloc" ON)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/snmalloc.cmake)

option(
TEST_HYBRID_TLS_GROUPS
"Assert that TLS handshakes negotiate a hybrid post-quantum group, which requires OpenSSL 3.5 or later"
OFF
)

option(CLANG_TIDY "Run clang-tidy on the codebase" OFF)

option(
Expand Down Expand Up @@ -881,10 +875,6 @@ if(BUILD_TESTS)

add_unit_test(tls_test ${CMAKE_CURRENT_SOURCE_DIR}/src/tls/test/main.cpp)
target_link_libraries(tls_test PRIVATE ${CMAKE_THREAD_LIBS_INIT})
target_compile_definitions(
tls_test
PRIVATE TEST_HYBRID_TLS_GROUPS=$<BOOL:${TEST_HYBRID_TLS_GROUPS}>
)

add_unit_test(
base64_test
Expand Down Expand Up @@ -1360,13 +1350,11 @@ if(BUILD_TESTS)
BUCKET bucket_c
)

if(TEST_HYBRID_TLS_GROUPS)
add_e2e_test(
NAME tls_groups_test
PYTHON_SCRIPT ${CMAKE_SOURCE_DIR}/tests/tls_groups.py
BUCKET bucket_a
)
endif()
add_e2e_test(
NAME tls_groups_test
PYTHON_SCRIPT ${CMAKE_SOURCE_DIR}/tests/tls_groups.py
BUCKET bucket_a
)

if(CLIENT_PROTOCOLS_TEST)
add_e2e_test(
Expand Down
7 changes: 1 addition & 6 deletions src/tls/test/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,10 +526,6 @@ class InspectableServer : public tls::Server
}
};

#ifndef TEST_HYBRID_TLS_GROUPS
# define TEST_HYBRID_TLS_GROUPS 0
#endif

// Hybrid groups offered by src/tls/context.h, in the order they are offered
constexpr auto secp384r1_mlkem1024 = "SecP384r1MLKEM1024";
constexpr auto secp256r1_mlkem768 = "SecP256r1MLKEM768";
Expand Down Expand Up @@ -607,8 +603,7 @@ TEST_CASE("group negotiation")
}
}

TEST_CASE(
"hybrid group negotiation" * doctest::skip(TEST_HYBRID_TLS_GROUPS == 0))
TEST_CASE("hybrid group negotiation")
{
const std::vector<std::string> hybrid_groups = {
secp384r1_mlkem1024, secp256r1_mlkem768, x25519_mlkem768};
Expand Down
1 change: 1 addition & 0 deletions tests/ci-buckets.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
bucket_a:
tls_groups_test
lts_compatibility

bucket_b:
Expand Down
25 changes: 23 additions & 2 deletions tests/tls_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,24 @@
WEAKEST_GROUP = "P-256"
WEAKEST_GROUP_REPORTED = "prime256v1"

# TLS NamedGroup IDs reported by OpenSSL 3.3
Comment thread
achamayou marked this conversation as resolved.
# Match IANA Group IDs defined in
# https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
GROUP_IDS = {
Comment thread
achamayou marked this conversation as resolved.
"23": WEAKEST_GROUP_REPORTED,
"4587": "SecP256r1MLKEM768",
"4588": "X25519MLKEM768",
"4589": "SecP384r1MLKEM1024",
}

# A group CCF does not offer
UNSUPPORTED_GROUP = "X448"

# s_client reports hybrid groups by name, and classical groups as a peer key
# s_client output varies between OpenSSL 3.3 and 3.5
NEGOTIATED_GROUP = re.compile(r"^Negotiated TLS1\.3 group: (\S+)$", re.MULTILINE)
TRACED_GROUP_ID = re.compile(
r"extension_type=key_share\(51\),[^\n]*\n" r"\s+NamedGroup: [^\n]*\((\d+)\)"
)
PEER_TEMP_KEY = re.compile(r"^Peer Temp Key: ECDH, ([^,]+),", re.MULTILINE)


Expand All @@ -29,7 +42,7 @@ def negotiate_group(address, groups):
group they agreed on, or None if they could not agree.
"""
completed = subprocess.run(
["openssl", "s_client", "-connect", address, "-groups", groups],
["openssl", "s_client", "-trace", "-connect", address, "-groups", groups],
input="",
capture_output=True,
text=True,
Expand All @@ -42,6 +55,14 @@ def negotiate_group(address, groups):
if match is not None and match.group(1) != "<NULL>":
return match.group(1)

for record in output.split("Received TLS Record")[1:]:
if "ServerHello," in record:
match = TRACED_GROUP_ID.search(record)
if match is not None:
group_id = match.group(1)
return GROUP_IDS.get(group_id, group_id)
break

match = PEER_TEMP_KEY.search(output)
if match is not None:
return match.group(1)
Expand Down