Add tls.allowUnperformableCiphers, because the restriction is itself a fingerprint - #44
Merged
Merged
Conversation
…a fingerprint
1.9.0 started refusing cipher lists containing suites this package cannot perform. The reasoning was
sound and the default is right, but shipping it with no escape hatch removed a capability that a
fingerprint package specifically needs, and it did so without saying what it cost.
What it costs, from the captures in this repo:
curl 8.21.0 offers 30 suites, 7 performable here
Chromium offers 15 suites, 7 performable here
A hello restricted to what can be honoured therefore carries a cipher list less than half the length
of any real client's — and list length and contents are precisely what a JA3 hash reads. Refusing to
offer an unperformable suite avoids a rare handshake failure by guaranteeing a permanent fingerprint
mismatch. For this package that is very likely the worse trade, and it is not one to make on a
caller's behalf in a minor release.
`tls.allowUnperformableCiphers` offers the accurate list. The risk is narrow and quantified: the
first unperformable suite sits at index 5 of curl's list and 7 of Chromium's, behind the TLS 1.3
suites, so a server with 1.3 available never reaches one. If a server does select one, the handshake
fails — and the negotiation error no longer calls that "a bug in the offer list", because with this
option set it is not a bug, it is the documented consequence of a deliberate choice. The message now
names the option so the next person to hit it does not go looking for a defect that is not there.
The default is unchanged: without the flag, an unperformable suite is still refused at configuration
time rather than discovered on the wire.
The README now states what the gap is made of, because most of it is not a missing feature. Sixteen
of curl's twenty-three are CBC — MAC-then-encrypt, unimplementable without a Lucky13 padding oracle
in JavaScript — and two more are RSA key exchange with no forward secrecy. Those are refusals this
package intends to keep. Three are implementable: the TLS 1.2 ChaCha20-Poly1305 suites, whose AEAD
is already injectable for 1.3. They are named as not-yet-implemented rather than left looking like
part of the same refusal.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1.9.0 started refusing cipher lists containing suites this package cannot perform. The reasoning was
sound and the default is right, but shipping it with no escape hatch removed a capability a
fingerprint package specifically needs — and did so without saying what it cost.
What it cost, from the captures in this repo
A hello restricted to what can be honoured carries a cipher list less than half the length of any
real client's — and list length and contents are precisely what a JA3 hash reads.
Refusing to offer an unperformable suite avoids a rare handshake failure by guaranteeing a
permanent fingerprint mismatch. For this package that is very likely the worse trade, and not one
to make on a caller's behalf in a minor release.
The escape hatch, and its quantified risk
tls.allowUnperformableCiphersoffers the accurate list. The first unperformable suite sits atindex 5 of curl's list and 7 of Chromium's, behind the TLS 1.3 suites — so a server with 1.3
available never reaches one.
If a server does select one, the handshake fails, and the negotiation error no longer calls that
"a bug in the offer list". With this option set it is not a bug; it is the documented consequence
of a deliberate choice, and the message names the option so the next person to hit it does not go
hunting for a defect that is not there.
The default is unchanged.
What the gap is actually made of
Most of it is not a missing feature:
in JavaScript
Both are refusals this package intends to keep. Three are implementable — the TLS 1.2
ChaCha20-Poly1305 suites, whose AEAD is already injectable for 1.3 — and the README now names them
as not-yet-implemented rather than leaving them to look like part of the same refusal.
Version → 1.10.0. 1257 offline tests pass.
🤖 Generated with Claude Code