Skip to content

Conversation

@UnaPibaGeek
Copy link

Expose negotiated TLS 1.3 key exchange group in supported_versions

Summary

This PR extends the TLS handshake logging to expose the negotiated key exchange group (CurveID / NamedGroup) for TLS 1.3 connections under:

handshake_log.server_hello.supported_versions.selected_version.key_exchange

In TLS 1.3, cipher suites no longer encode the key exchange mechanism, which makes it impossible for consumers to determine whether a server negotiated X25519, secp256r1, etc.
zcrypto already parses this information internally from the ServerHello, but it was not previously exposed in the JSON output.

This change makes that information available in a backward-compatible way.


Motivation / Rationale

In TLS ≤ 1.2, the key exchange mechanism can be inferred from the cipher suite name (e.g. ECDHE_RSA_* vs RSA_*).
However, in TLS 1.3, cipher suites are decoupled from the key exchange group (RFC 8446), and all TLS 1.3 cipher suites are AEAD-only.

As a result, consumers of zcrypto (e.g. zgrab2 and downstream tooling) currently cannot determine which key exchange group was actually negotiated for TLS 1.3 connections, even though this information is security-relevant (e.g. X25519 vs P-256, crypto posture analysis, TLS hardening, future PQ transition work).

zcrypto already parses:

  • the negotiated group from the ServerHello key_share extension, and
  • the selected group from the HelloRetryRequest path,

but this information was not surfaced in the handshake log.

This PR closes that gap.


Changes

  • Introduce an extensible wrapper for supported_versions.selected_version that preserves the existing name and value fields.
  • When a TLS 1.3 connection is negotiated, populate an optional key_exchange field with the negotiated CurveID:
    • Prefer serverShare.group when present.
    • Fall back to selectedGroup for the HelloRetryRequest path.
  • Keep the field omitted when the information is not available.
  • Update the JSON schema accordingly.

The output remains fully backward compatible for existing consumers.


Example Output zgrab2 (TLS 1.3)

"supported_versions": {
  "selected_version": {
    "name": "TLSv1.3",
    "value": 772,
    "key_exchange": {
      "hex": "0x001D",
      "name": "ecdh_x25519",
      "value": 29
    }
  }
}

Compatibility & Impact

  • No changes to existing fields or semantics.
  • key_exchange is optional and only appears when applicable.
  • No impact on TLS 1.2 or earlier handshakes.
  • No changes required in zgrab2 or other consumers unless they want to consume the new field.
@UnaPibaGeek
Copy link
Author

Rebased on latest master to keep the PR up-to-date.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant