sftcd / pemesni

PEM files for ESNI
BSD 2-Clause "Simplified" License
0 stars 1 forks source link

Enforcement of singular ECHConfig in EchConfigList #4

Open cpu opened 4 months ago

cpu commented 4 months ago

The draft as written today prescribes that the .pem content between the "BEGIN ECHCONFIG" and "END ECHCONFIG" boundaries MUST represent "an ECHConfigList containing exactly one ECHConfig".

What's the background on this requirement? Could it be relaxed, or handled differently? What if the EchConfigList were allowed to contain multiple EchConfig provided they all specify the same public key matching the PKCS#8 private key encoded adjacent to the config list.

sftcd commented 4 months ago

Background is twofold, but I may be misremembering as it's been a while, so I don't consider these as settled issues, esp. since I've not gotten much review 'till yours.

cpu commented 4 months ago

ECHConflgList encoding uses TLS encoding so there's no on-the-wire diff between an ECHConfigList with one entry and a singluar ECHConfig

I remember you mentioning this in Matrix, but I don't believe it's true. An ECHConfigList with one entry still needs the list length (in total bytes, not number of elements) prefixed to it. A singular ECHConfig would not have that prefix. This was the source of my confusion at the time of that discussion: I was using a DNS-over-HTTPS library that was stripping the TLS list length confusing my deserialization as a TLS encoded list.

E.g. we can look at a value I pulled from defo.ie's records at some point in the past, a ECHConfigList with one EchConfig:

[0x0, 0x40, 0xfe, 0xd, 0x0, 0x3c, 0xb9, 0x0, 0x20, 0x0, 0x20, 0x88, 0xc4, 0x6, 0xd4, 0xbe, 0xa1, 0xae, 0x9e, 0x7, 0x9f, 0xe4, 0x67, 0xb2, 0x1a, 0xf8, 0x78, 0x49, 0x3a, 0x1d, 0x5, 0x76, 0x2e, 0x6b, 0x22, 0x89, 0x3d, 0x2e, 0x8f, 0x64, 0x28, 0x5e, 0x1d, 0x0, 0x4, 0x0, 0x1, 0x0, 0x1, 0x0, 0xd, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x2e, 0x64, 0x65, 0x66, 0x6f, 0x2e, 0x69, 0x65, 0x0, 0x0]

A single EchConfig expressed outside the ECHConfigList syntax would be:

[0xfe, 0xd, 0x0, 0x3c, 0xb9, 0x0, 0x20, 0x0, 0x20, 0x88, 0xc4, 0x6, 0xd4, 0xbe, 0xa1, 0xae, 0x9e, 0x7, 0x9f, 0xe4, 0x67, 0xb2, 0x1a, 0xf8, 0x78, 0x49, 0x3a, 0x1d, 0x5, 0x76, 0x2e, 0x6b, 0x22, 0x89, 0x3d, 0x2e, 0x8f, 0x64, 0x28, 0x5e, 0x1d, 0x0, 0x4, 0x0, 0x1, 0x0, 0x1, 0x0, 0xd, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x2e, 0x64, 0x65, 0x66, 0x6f, 0x2e, 0x69, 0x65, 0x0, 0x0]

The 0x00 0x40 prefix in the first is the TLS encoded list length. The singular EchConfig is the next 0x40 bytes, beginning with 0xFE 0xD - the uint16 version; field of the ECHConfig structure: (0xFE0D).

It would be wrong to include the 0x00 0x40 prefix for an ECHConfig, and it would be wrong to exclude that prefix for a ECHConfigList with one ECHConfig. They're not the same encoding.

sftcd commented 4 months ago

Ah sorry, yeah I mispoke. Yes you're correct above. If there's >1 public key in the list though it still only has one outer length.

But in any case, it's only really an encoded ECHConfigList that's useful, boring, wolfssl and my openssl fork all process that as an input and would barf if the leading outer length were missing IIRC.

cpu commented 4 months ago

If there's >1 public key in the list though it still only has one outer length.

Agreed.

boring, wolfssl and my openssl fork all process that as an input and would barf if the leading outer length were missing IIRC.

We would do the same in Rustls so I agree a misencoding would be caught quickly. I just wonder if the delimiter pushes you towards making, and then fixing, that error instead of doing the right thing from the jump.

I know I'm also painting the bike shed here 😅