intarchboard / program-edm

Evolvability, Deployability, & Maintainability Program
0 stars 0 forks source link

Can greasing mechanisms avoid reserving or coordinating "fake" values? #7

Open tfpauly opened 3 years ago

tfpauly commented 3 years ago

In TLS, GREASE (https://www.rfc-editor.org/rfc/rfc8701.html) reserves several values in different extension code point spaces to be used for greasing—that is, endpoints will randomly select some of the reserved values to send to the peer in order to ensure that the peer can handle unknown values.

This is also described in the use-it-or-lose-it draft which EDM is looking at (https://intarchboard.github.io/use-it-or-lose-it/draft-iab-use-it-or-lose-it.html#rfc.section.4.3). The description here also refers to the approach of reserving these values.

For existing protocols like TLS, reserving values seems to be the safest option. The downside is that these values represent only a finite subset of possible values; it is possible to still ossify to allow reserved greased values, but not allow others (if your implementation is particularly obstructive). It is also necessary to choose a wide distribution of values that ensure that the entire range of possible values is kept from ossifying.

Reservation of values is harder for other kinds of extension points. For example, the proposed HTTP greasing (https://tools.ietf.org/html/draft-nottingham-http-grease-01) discusses greasing header names and values. Since these are strings and other arbitrary values, simply reserving these ahead of time doesn't make sense. The proposal here is to have a coordination effort where greased values are agreed upon by the community.

If we are looking at recommendations for new protocol design, I wonder if we can come up with recommendations that achieve the goals of greasing without needing to rely on either reservation or coordination.

A goal we could have is that an endpoint can (and would) generate random or unknown values for various extension points to prevent ossification; but not risk causing a peer that actually does understand and handle that value to think that the endpoint is trying to use a protocol feature. Put another way, the random value should be indistinguishable from a valid value to the receiver of the value that doesn't support the valid version of the value; but a receiver that does understand the valid version of the value should be able to distinguish a greased/random value from an intentional value.

Consider an extension point like the ones that are greased in TLS, where the values are numbers within a range (say, 16-bit integers). Rather than just using these as Types in a Type-Length-Value scheme, the encoding could also include a value that proves if the sender is using a type based on a given version of a standard, or is just sending random data. Call this Type-Length-Hash-Value. In this case, when a given type is reserved, it could also define a unique pattern that can be hashed with something that changes in a message where the extension appears (like a nonce). If the receiver also knows the same unique pattern, it could confirm that the sender has the same understanding of the type by calculating the hash. A receiver that doesn't understand the type wouldn't be able to tell whether or not this was greased or valid.

A similar approach could be taken when the extensible values are strings, etc, allowing implementations to send random values without coordination.

martinthomson commented 3 years ago

I think that this comes down to collision probability. Which means that you need to consider the size of the space. Most numeric codepoints don't have the luxury of a sufficiently large space that random sampling would be safe. QUIC's 2^62 values is somewhat on the edge of what you might consider to be reasonable here; you probably need something closer to twice that many bits to be fully confident that a collision won't occur, but the odds are enough that you might risk it.

HTTP stands a pretty good chance of pulling this sort of thing off. Strings have practically unbounded space available, so you can tune your tolerance for collisions. A 20 character string, randomly selected, has a negligible chance of collision.

TLHV is an interesting idea, but I don't think that it reflects a good trade-off. If this is the way that you define all extensions, then all legitimate extensions will have to pay the byte cost of the hash. This might be relatively small, as it only exists to counter the collision probability over the code-point space, but it's still a non-zero overhead for every extension.

For TLS, where the typical handshake has grown to somewhere between 10 and 20 extensions, and you probably need in the order of 8 bytes, that's a non-trivial overhead. If you consider valid extensions to be dominant, then this overhead disproportionately affects - and maybe even disincentivizes - valid uses. We see that already with the 4 byte overhead for T and L.

But if we step back, I'm not sure that the goal is to remove coordination. I know that it is tempting to engineer inconvenient components of the system out, and coordination is something we generally carefully. In this case, I think that the opposite might apply. Our ability to coordinate is a superpower. The greasing schemes proposed thus far don't take sufficient advantage of that.

Consider: it is easy to filter out grease as defined in QUIC or TLS. That runs counter to our goal. Consider instead a greasing scheme that said that in the month of November 2020 we would all include "X". That would achieve the same end as having arbitrary reserved codepoints. We could even later use "X" for something else.

mnot commented 3 years ago

I think considering the goal(s) is worthwhile, because it affects how the consuming implementations (that we're trying to change behaviour in, presumably) handle the grease, but also how they interact with and perceive the greasing effort (especially if it's not designed into the protocol from the start).

E.g., if the goal is to allow new extensions to deployed without any friction or coordination (e.g., announcements), random values might be one way to realise that -- but it could be perceived as adversarial (e.g., with systems who use unknown values as a sign of a security or other issue).

OTOH if the goal is to make it possible to introduce new extensions -- possibly with some coordination -- by assuring that recipients have the capacity and systems in place to introduce and deploy a change in a reasonable amount of time (browser evergreen comes to mind here), it might be adequate to just announce and deploy a new extension periodically (whether or not one is actually needed).

martinthomson commented 3 years ago

That seems a little like accepting that we don't have freedom to extend arbitrarily. If that is true (and it could well be for some protocols), then I would be sad. I think that recognizing that possibility is fine, particularly for protocols with a deployed base and uneven treatment of extensions.

However, as much as I value coordination, it seems like a poor way to manage essential protocol functions. Of which I regard extension as one, assuming that the mechanism has been designed to avoid that sort of strong coupling. Advising against randomizing would be a pretty severe impediment on functions designed to operate without central coordination or control. If the protocol has been designed to allow arbitrary extension, then it is reasonable - not at all adversarial - to exercise that capability.

In the end, this is about managing that tension between permission-less innovation and developing systems for positive coordination. I would prefer to frame this as the latter supporting the former. The idea that coordination might become obligatory or necessary is not something I would support. At least not for everyone.

There might be a different approach needed for those deploying at scale. When curl changes, when Chrome changes, or when a CDN changes, those changes can affect a lot of people. I don't think that the people responsible need further encouragement to understand the value that coordination delivers in that context though.

tfpauly commented 3 years ago

I agree with @martinthomson that we retain the freedom to extend arbitrarily, at least in new protocol designs.

I'd like to find a way to design coordination mechanisms to benefit from the power of coordination—our superpower—without restricting people's ability to innovate independently, and also to be able to have implementations that fall behind without breaking.

What that's pointing towards, to me, is having some way to indicate what coordination a certain endpoint is aware of. (This would replace any need for the H in TLHV mentioned above, by making it more session-wide.)

Here's a thought experiment:

Say you have a registry of extensions, and every time the registry is updated to add some extensions, there is a new, unpredictable version name of the registry. Any implementation that updates to use new extensions from that registry would then be in a position where they're expected to be aware of the possible valid values.

When a client issues a request, it includes a list of hashed version names that it supports.

Then, it would send any extensions that it wants, along with any random/greased extensions are not overlapping with allocated values in the latest version it claims. An endpoint that receives this request would be able to check to see which version both it and the client knows about, and correctly ignore random/greased extensions. And, if the receiver doesn't understand any version, the exchange fails.

If you want to have a private version that uses custom extensions, that value can be agreed upon between client and server.