Skip to content

Implement RFC 8701 GREASE for TLS ClientHello#30303

Closed
mcrmck wants to merge 7 commits into
openssl:masterfrom
mcrmck:rfc8701-grease
Closed

Implement RFC 8701 GREASE for TLS ClientHello#30303
mcrmck wants to merge 7 commits into
openssl:masterfrom
mcrmck:rfc8701-grease

Conversation

@mcrmck

@mcrmck mcrmck commented Mar 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements client-side RFC 8701 GREASE (Generate Random Extensions And Sustain Extensibility) for OpenSSL, addressing #9660 which has been open since 2019.

This picks up where #14560 left off, incorporating the review feedback from @davidben and @paulidale on that PR:

  • Keep the API internal (davidben): ossl_grease_value() is internal to libssl, not exported. The GREASE index constants are #defines in ssl_local.h, not in public headers.
  • No enums in public headers (paulidale): All GREASE constants use #define, not enum.
  • Store on SSL, not SSL_SESSION (davidben): The grease_seed[] lives in ssl_connection_st.ext, not in SSL_SESSION, so GREASE values are not carried across resumptions.
  • Idempotent seeding (paulidale): ossl_grease_value() lazy-seeds once via RAND_bytes and returns the same value on repeated calls, ensuring consistency across HelloRetryRequest retransmissions.

What is GREASE?

TLS endpoints that reject unknown values cause ecosystem ossification — new extensions and cipher suites can't be deployed because buggy implementations choke on them. GREASE prevents this by having clients routinely send reserved meaningless values (matching the 0x?A?A pattern) in ClientHello fields. Well-behaved servers ignore them; broken servers fail visibly.

GREASE injection points

When SSL_OP_GREASE is set on a client connection:

ClientHello field Position Details
Cipher suites Prepended One GREASE cipher suite before real ciphers
Supported versions Prepended One GREASE version before real versions
Supported groups Prepended One GREASE group before real groups
Signature algorithms Appended One GREASE sigalg after real sigalgs
Key share Prepended GREASE group + 1 zero byte of key_exchange data
Extension 1 In extensions list Empty extension (0 bytes of data)
Extension 2 In extensions list Non-empty extension (1 zero byte of data)

The client also rejects GREASE values in server responses (ServerHello key_share group). Server-side tolerance already works — tls_collect_extensions() ignores unknown extension types, and cipher/group/version negotiation naturally skips unknown values.

Design decisions and differences from BoringSSL

This implementation closely follows BoringSSL's GREASE, which was written by the RFC author (@davidben). Key alignment:

  • Same grease_seed[] + lazy RAND_bytes seeding pattern
  • Same 0x?A?A value generation from seed bytes
  • GREASE values prepended in cipher suites, versions, groups, and key share
  • Extension 2 carries 1 byte of data (extension 1 is empty)
  • Key share GREASE entry uses a zero byte

One deliberate difference: This implementation also GREASEs signature_algorithms, which BoringSSL does not do. RFC 8701 Section 3.1 explicitly lists SignatureScheme values as a MAY field for GREASE. We include it for more thorough extensibility testing, but this could be removed if reviewers prefer strict alignment with BoringSSL.

Files changed (13 files, +399/-2)

  • ssl/ssl_local.h — GREASE index defines, seed storage in ssl_connection_st, helper declarations
  • include/openssl/ssl.h.inSSL_OP_GREASE option flag (bit 41)
  • ssl/ssl_lib.cossl_grease_value() helper with lazy seeding and collision avoidance
  • ssl/ssl_ciph.c — GREASE cipher suite injection in ssl_cipher_list_to_bytes()
  • ssl/statem/extensions_clnt.c — GREASE in supported_versions, supported_groups, sig_algs, key_share; GREASE extension construct functions; GREASE rejection in stoc key_share parser
  • ssl/statem/extensions.cext_defs[] entries for GREASE extensions
  • ssl/statem/statem_local.h — Sentinel type defines and function declarations
  • apps/s_client.c-grease CLI option
  • doc/man1/openssl-s_client.pod.in — s_client documentation
  • doc/man3/SSL_CTX_set_options.podSSL_OP_GREASE documentation
  • CHANGES.md — User-visible change note
  • test/ext_internal_test.c — Extension ordering test entries
  • test/sslapitest.c — GREASE functional test (msg_callback-based ClientHello parsing, handshake completion)

Testing

  • make test TESTS=test_internal_exts — PASS (extension ordering)
  • make test TESTS=test_sslapi — PASS (GREASE test verifies values in captured ClientHello + successful handshake)
  • make test TESTS=test_ssl_new — PASS (no regressions)
  • Manual: openssl s_client -grease -connect google.com:443 completes TLS 1.3 handshake successfully

@openssl-machine openssl-machine added the hold: cla required The contributor needs to submit a license agreement. label Mar 8, 2026
@openssl-machine openssl-machine added approval: review pending This pull request needs review by a committer and removed hold: cla required The contributor needs to submit a license agreement. labels Mar 8, 2026

@mattcaswell mattcaswell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice work!

Comment thread ssl/statem/extensions_clnt.c Outdated
Comment thread ssl/statem/extensions_clnt.c Outdated
Comment thread CHANGES.md Outdated
Comment thread test/sslapitest.c Outdated
Comment thread test/sslapitest.c Outdated
Comment thread test/sslapitest.c Outdated
@t8m t8m added branch: master Applies to master branch triaged: feature The issue/pr requests/adds a feature tests: present The PR has suitable tests present labels Mar 10, 2026
@mcrmck
mcrmck requested a review from mattcaswell March 11, 2026 08:34
@mcrmck

mcrmck commented Mar 11, 2026

Copy link
Copy Markdown
Contributor Author

Thank you very much for your feedback, @mattcaswell . I have done my best to incorporate and resubmit for your review.

One small clarification question:
For grease1 (the empty extension), I kept WPACKET_put_bytes_u16(pkt, 0) since that matches the existing pattern for zero-length extensions (e.g. tls_construct_ctos_etm. Let me know if you'd prefer WPACKET_start_sub_packet_u16 / WPACKET_close there as well for consistency. Thanks.

@mattcaswell mattcaswell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For grease1 (the empty extension), I kept WPACKET_put_bytes_u16(pkt, 0) since that matches the existing pattern for zero-length extensions (e.g. tls_construct_ctos_etm. Let me know if you'd prefer WPACKET_start_sub_packet_u16 / WPACKET_close there as well for consistency. Thanks.

You could go either way with that - but I'm fine with this.

Comment thread test/sslapitest.c Outdated
@mattcaswell

Copy link
Copy Markdown
Member

The CI failures look relevant.

@mcrmck
mcrmck requested a review from mattcaswell March 11, 2026 15:30
mattcaswell
mattcaswell previously approved these changes Mar 11, 2026

@mattcaswell mattcaswell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

@mattcaswell

Copy link
Copy Markdown
Member

Ping @openssl/committers for second review.

@mattcaswell
mattcaswell requested a review from t8m March 13, 2026 16:23

@t8m t8m left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also please rebase this to resolve the conflict.

Comment thread ssl/ssl_lib.c Outdated
Comment thread CHANGES.md
mcrmck added 6 commits March 13, 2026 14:21
Add client-side GREASE (Generate Random Extensions And Sustain
Extensibility) support per RFC 8701. When SSL_OP_GREASE is set,
the TLS client injects reserved 0x?A?A-pattern values into the
ClientHello to prevent ecosystem ossification caused by servers
that reject unknown values.

GREASE values are injected into:
- Cipher suites (prepended)
- Supported versions extension (prepended)
- Supported groups extension (prepended)
- Signature algorithms extension (appended)
- Key share extension (prepended, 1 zero byte)
- Two standalone extensions (one empty, one with 1 zero byte)

The implementation uses lazy-seeded random values that remain
consistent across HelloRetryRequest retransmissions. GREASE values
from server responses are rejected as illegal parameters.

Add -grease option to s_client to enable GREASE from the command line.

Closes openssl#9660
Wrap the GREASE test functions and static variables in
#if !defined(OSSL_NO_USABLE_TLS1_3) to match the existing guard on
the ADD_TEST call. Fixes build failures with -Werror on no-tls,
no-tls1_3, and minimal (no-bulk) configurations where TLS 1.3 is
unavailable and the test functions were unused.
- Use WPACKET_start_sub_packet_u16/WPACKET_close instead of manually
  writing u16 length in key_share GREASE entry and GREASE extension 2
- Update CHANGES.md author attribution
- Rewrite GREASE test to use PACKET functions (PACKET_get_net_2,
  PACKET_get_length_prefixed_*, etc.) instead of raw pointer arithmetic,
  following the pattern in clienthellotest.c
- Replace magic numbers with SSL3_HM_HEADER_LENGTH, CLIENT_VERSION_LEN,
  and SSL3_RANDOM_SIZE macros
- Expand test coverage to verify GREASE values in supported_groups,
  key_shares, signature_algorithms, and supported_versions in addition
  to cipher suites and extensions
- Initialize PACKET structs with memset to fix GCC
  -Werror=maybe-uninitialized errors on CI
- Initialize ext_type and val variables to zero
- Fix continuation line indentation to match clang-format
- Expect exactly 2 GREASE extensions (grease1 and grease2)
  instead of >= 1
- Use RAND_bytes_ex() with libctx from SSL_CTX associated with the
  SSL_CONNECTION for GREASE seed generation, as requested by t8m.
- Move GREASE CHANGES.md entry from "3.6 to 4.0" to "4.0 to 4.1"
  section, as requested by t8m.
@mcrmck

mcrmck commented Mar 13, 2026

Copy link
Copy Markdown
Contributor Author

@t8m thank you for the feedback, all should be addressed in 461aae7

@mcrmck
mcrmck requested a review from t8m March 13, 2026 18:40
- Reformat RAND_bytes_ex() call in ossl_grease_value() per clang-format
- Fix || continuation indent in test_session_cache_overflow (upstream
  commit introduced a style violation picked up by our changed file)
@mcrmck
mcrmck requested a review from mattcaswell March 16, 2026 11:07
@openssl-machine openssl-machine added approval: done This pull request has the required number of approvals approval: ready to merge The 24 hour grace period has passed, ready to merge and removed approval: review pending This pull request needs review by a committer approval: done This pull request has the required number of approvals labels Mar 16, 2026
@openssl-machine

Copy link
Copy Markdown
Collaborator

This pull request is ready to merge

@nhorman

nhorman commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

conflicts resolved, squashed and merged to master, thank you

@nhorman nhorman closed this Mar 17, 2026
openssl-machine pushed a commit that referenced this pull request Mar 17, 2026
Add client-side GREASE (Generate Random Extensions And Sustain
Extensibility) support per RFC 8701. When SSL_OP_GREASE is set,
the TLS client injects reserved 0x?A?A-pattern values into the
ClientHello to prevent ecosystem ossification caused by servers
that reject unknown values.

GREASE values are injected into:
- Cipher suites (prepended)
- Supported versions extension (prepended)
- Supported groups extension (prepended)
- Signature algorithms extension (appended)
- Key share extension (prepended, 1 zero byte)
- Two standalone extensions (one empty, one with 1 zero byte)

The implementation uses lazy-seeded random values that remain
consistent across HelloRetryRequest retransmissions. GREASE values
from server responses are rejected as illegal parameters.

Add -grease option to s_client to enable GREASE from the command line.

Closes #9660

Reviewed-by: Tomas Mraz <[email protected]>
Reviewed-by: Matt Caswell <[email protected]>
Reviewed-by: Neil Horman <[email protected]>
MergeDate: Tue Mar 17 14:58:25 2026
(Merged from #30303)
@mattcaswell

Copy link
Copy Markdown
Member

Thanks for the contribution @mcrmck!

@mattcaswell mattcaswell mentioned this pull request Apr 21, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approval: ready to merge The 24 hour grace period has passed, ready to merge branch: master Applies to master branch tests: present The PR has suitable tests present triaged: feature The issue/pr requests/adds a feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants