Skip to content

Draw the GREASE signature algorithm from the per-connection seed - #9

Merged
bogdanfinn merged 1 commit into
bogdanfinn:masterfrom
nihiloid:grease-signature-algorithms
Sep 2, 2026
Merged

Draw the GREASE signature algorithm from the per-connection seed#9
bogdanfinn merged 1 commit into
bogdanfinn:masterfrom
nihiloid:grease-signature-algorithms

Conversation

@nihiloid

Copy link
Copy Markdown

Cherry-pick of refraction-networking/utls#412 onto this fork. The two hunks apply to master (v1.7.7-barnius) without change.

Problem

Chrome 152 introduced a GREASE value in the signature_algorithms extension. Chrome sends that value first in the list and draws a new value for each connection. Chrome 151 and earlier sent no GREASE value there.

utls replaces GREASE placeholders in the cipher suites, the supported groups, the key shares, the supported versions, and the GREASE extensions. It does not replace them in signature_algorithms. A spec that writes SignatureScheme(GREASE_PLACEHOLDER) therefore sends the literal value 0x0a0a on every connection. That constant is a reliable "this is not Chrome" signal. Downstream code works around this today by drawing an independent random GREASE value per connection, which can collide with the GREASE value in another position of the same ClientHello.

Change

  • u_tls_extensions.go: add the seed index ssl_grease_signature_algorithm. It comes before ssl_grease_ticket_extension, so the indexes of the older positions keep their values. ssl_grease_last_index grows by one, and UConn.greaseSeed grows with it.
  • u_parrots.go: add a case *SignatureAlgorithmsExtension to the reGREASE switch in ApplyPreset. It replaces each entry for which isGREASEUint16 reports true with GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_signature_algorithm), the same way the existing curve, key share, and version cases work. The value now comes from the one per-connection seed, so the GREASE values in one ClientHello relate to each other the way BoringSSL relates them.
  • u_grease_sigalg_test.go: new tests. They check that the placeholder becomes a 0x?a?a value, that the other signature algorithms stay unchanged, that the value changes across 64 connections, and that the value equals the seeded value for the new index.

This change adds the mechanism only. No bundled profile writes SignatureScheme(GREASE_PLACEHOLDER) in signature_algorithms, so every existing profile sends the same bytes as before. A caller that builds its own ClientHelloSpec can use it now.

SignatureAlgorithmsCertExtension gets no substitution, because BoringSSL does not GREASE it.

Test

go build ./..., go vet ./..., and go test ./... pass on this branch.

Add the seed index ssl_grease_signature_algorithm. ApplyPreset now replaces the
GREASE placeholder in SignatureAlgorithmsExtension with the seeded value.

Add u_grease_sigalg_test.go for the substitution, the per-connection draw, and
the seed index.
@bogdanfinn
bogdanfinn merged commit 1bc2974 into bogdanfinn:master Sep 2, 2026
@nihiloid
nihiloid deleted the grease-signature-algorithms branch September 4, 2026 07:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants