Skip to content

refactor(harness): Extend handshake logic to support TLS 1.2#5614

Merged
kaukabrizvi merged 10 commits intoaws:mainfrom
kaukabrizvi:DRS_TLS_1.3
Nov 17, 2025
Merged

refactor(harness): Extend handshake logic to support TLS 1.2#5614
kaukabrizvi merged 10 commits intoaws:mainfrom
kaukabrizvi:DRS_TLS_1.3

Conversation

@kaukabrizvi
Copy link
Copy Markdown
Contributor

@kaukabrizvi kaukabrizvi commented Nov 12, 2025

Goal

Add TLS 1.2 support to the in-memory handshake driver by replacing the fixed two round trips logic with a single, version-agnostic loop that drives both peers until completion and exits only when no further progress is observed.

Why

The previous approach assumed TLS 1.3 timing (2 RTT) and could fail with TLS 1.2/HRR patterns. We would prefer one robust path that works for both TLS 1.2 and TLS 1.3 without special-casing. Currently, the dynamic record sizing test only supports TLS 1.3, causing it to fail on OpenSSL 1.0.2, which uses TLS 1.2.

How

Replace fixed 2-RTT assumption with while !handshake_completed() driving client/server.

Callouts

A top-level while !handshake_completed() alone isn’t sufficient: one peer can still have post-handshake bytes in flight (TLS 1.2) when sending application data, leading to S2N_ERR_BAD_MESSAGE.

Waiting for empty transcripts can hang in TLS 1.3 because servers may queue post-handshake messages (e.g., NewSessionTicket) that are consumed during app reads, not another handshake tick.

Using a progress-based exit avoids both issues: we don’t require emptiness (no hang), and we don’t stop early while bytes are still moving.

Testing

I ran the integration tests in a nix-based OpenSSL 1.0.2 shell (negotiating TLS 1.2) and OpenSSL 3.0 shell (negotiating TLS 1.3). Both tests pass.

Related

This PR is a follow-up to #5608, which should test dynamic record sizing across protocol versions but currently only supports TLS 1.3 handshakes. See comment here.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@github-actions github-actions bot added the s2n-core team label Nov 12, 2025
@kaukabrizvi kaukabrizvi marked this pull request as ready for review November 12, 2025 18:06
let mut progress = true;

// Keep looping while handshake not complete or progress is still being made
while !self.handshake_completed() || progress {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is the progress field necessary? In my mind, it feels like just !self.handshake_completed() should be sufficient?

Copy link
Copy Markdown
Contributor Author

@kaukabrizvi kaukabrizvi Nov 12, 2025

Choose a reason for hiding this comment

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

I thought the same initially, but on TLS 1.2 the round trip in dynamic record sizing test hit:

S2N_ERR_BAD_MESSAGE: Bad message encountered (/tls/s2n_post_handshake.c:104)

That happened because one side still had handshake bytes in flight even after both reported completion. The progress check drains those remaining bytes before starting application data - after adding it, the round trip worked as intended. The alternative would be a separate helper for tests, but I preferred centralizing it here. Let me know what you think.

Copy link
Copy Markdown
Contributor Author

@kaukabrizvi kaukabrizvi Nov 15, 2025

Choose a reason for hiding this comment

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

Quick update - I’ve since removed the progress-tracking logic. I updated handshake_completed() to rely on the result of poll_negotiate() rather than the handshake type string, so now the loop exits correctly without needing to track progress. TLS 1.2 no longer leaves stray handshake bytes, and the dynamic record sizing test works as expected.

@kaukabrizvi kaukabrizvi requested a review from jmayclin November 12, 2025 19:31
@jouho
Copy link
Copy Markdown
Contributor

jouho commented Nov 12, 2025

Is there going to be a followup PR that adds the actual TLS1.2 test after this PR is merged, if so should we just include that change in this PR? How do I know that the TLS 1.2 handshake tests are going to work?

@kaukabrizvi
Copy link
Copy Markdown
Contributor Author

kaukabrizvi commented Nov 13, 2025

Is there going to be a followup PR that adds the actual TLS1.2 test after this PR is merged, if so should we just include that change in this PR? How do I know that the TLS 1.2 handshake tests are going to work?

@jouho The current dynamic record sizing test already exercises both TLS 1.2 and TLS 1.3 by performing a handshake agnostic of TLS 1.2 vs. TLS 1.3. As a result, the test currently fails for OpenSSL 1.0.2 (which negotiates TLS 1.2); you can see the failure here. With the changes in this PR, the test passes across all libcryptos - including OpenSSL 1.0.2 - indicating that the TLS 1.2 and TLS 1.3 handshake succeeds.

Copy link
Copy Markdown
Contributor

@jmayclin jmayclin left a comment

Choose a reason for hiding this comment

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

Approved after comment added.

@kaukabrizvi kaukabrizvi added this pull request to the merge queue Nov 17, 2025
@kaukabrizvi kaukabrizvi removed this pull request from the merge queue due to a manual request Nov 17, 2025
@kaukabrizvi kaukabrizvi added this pull request to the merge queue Nov 17, 2025
Merged via the queue into aws:main with commit 5cc3029 Nov 17, 2025
52 checks passed
@kaukabrizvi kaukabrizvi deleted the DRS_TLS_1.3 branch November 17, 2025 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants