Change:
ESNIConfig has a version number in front for extensibility, but it is unclear what the version invariants are. Presumably we would like versions to change the contents of an ESNIConfig (indeed different drafts have done so), but it is not obvious this is possible and it is impossible to skip over an ESNIConfig with a version you do not understand. Fix this by moving the body of an ESNIConfig into an ESNIConfigContents and adding a length prefix in front.
As of draft-ietf-tls-esni-04, when the server rejects ESNI with esniretryrequest, it includes one or more ESNIKeys for retry: The client behavior described in section 5.1.2 is to pick one of the ESNIKeys values from this vector: Encoding of contains the total size of the vector field but not the size of the individual ESNIKeys elements. This makes it difficult (impossible?) to parse a vector with unknown future versions, so this part is not really extensible.
Thanks for the comment! URL should resolve this. It's a little goofy-looking but ah well.
Thank you for confirming the issue. Not the nicest but will be effective. It's the only real blocking point I see when implementing draft -04.

Discussion:
with a version they do not understand.  [[NOTE: This means that the RFC will presumably have a nonzero value.]] <ins> An opaque byte string whose contents depend on the version of the structure.  For this specification, the contents are an ESNIConfigContents structure. The ESNIConfigContents structure contains the following fields: </ins> The non-empty name of the entity trusted to update these encryption keys.  This is used to repair misconfigurations, as described in handle-server-response.

Zero padding whose length makes the serialized PaddedServerNameList struct have a length equal to <del> ESNIConfig.padded_length. </del> <ins> ESNIConfigContents.padded_length. </ins> This value consists of the serialized ServerNameList from the "server_name" extension, padded with enough zeroes to make the total <del> structure ESNIConfig.padded_length bytes long.  The purpose of the padding is to prevent attackers from using the length of the </del> <ins> structure ESNIConfigContents.padded_length bytes long.  The purpose of the padding is to prevent attackers from using the length of the </ins> "encrypted_server_name" extension to determine the true SNI.  If the <del> serialized ServerNameList is longer than ESNIConfig.padded_length, the client MUST NOT use the "encrypted_server_name" extension. </del> <ins> serialized ServerNameList is longer than ESNIConfigContents.padded_length, the client MUST NOT use the "encrypted_server_name" extension. </ins> The ClientEncryptedSNI.encrypted_sni value is then computed using AEAD-Encrypt (RFC5116; Section 2.1) with the AEAD corresponding to

This value is placed in an "encrypted_server_name" extension. <del> The client MUST place the value of ESNIConfig.public_name in the "server_name" extension.  (This is required for technical conformance with RFC7540; Section 9.2.)  The client MUST NOT send a "cached_info" extension RFC7924 with a CachedObject entry whose </del> <ins> The client MUST place the value of ESNIConfigContents.public_name in the "server_name" extension.  (This is required for technical conformance with RFC7540; Section 9.2.)  The client MUST NOT send a "cached_info" extension RFC7924 with a CachedObject entry whose </ins> CachedInformationType is "cert", since this indication would divulge the true server name.

authenticating the connection for the origin server. If the value is "esni_retry_request", the client proceeds with the <del> handshake, authenticating for ESNIConfig.public_name as described in auth-public-name.  If authentication or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT use the retry keys. </del> <ins> handshake, authenticating for ESNIConfigContents.public_name as described in auth-public-name.  If authentication or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT use the retry keys. </ins> Otherwise, when the handshake completes successfully with the public name authenticated, the client MUST abort the connection

If the server negotiates an earlier version of TLS, or if it does not provide an "encrypted_server_name" extension in EncryptedExtensions, the client proceeds with the handshake, authenticating for <del> ESNIConfig.public_name as described in auth-public-name.  If an earlier version was negotiated, the client MUST NOT enable the False Start optimization RFC7918 for this handshake.  If authentication or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT treat this as a secure signal to disable ESNI. </del> <ins> ESNIConfigContents.public_name as described in auth-public-name.  If an earlier version was negotiated, the client MUST NOT enable the False Start optimization RFC7918 for this handshake.  If authentication or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT treat this as a secure signal to disable ESNI. </ins> Otherwise, when the handshake completes successfully with the public name authenticated, the client MUST abort the connection with an

decline ESNI-established sessions if it did not accept ESNI. The client MUST verify that the certificate is valid for <del> ESNIConfig.public_name.  If invalid, it MUST abort the connection with the appropriate alert. </del> <ins> ESNIConfigContents.public_name.  If invalid, it MUST abort the connection with the appropriate alert. </ins> If the server requests a client certificate, the client MUST respond with an empty Certificate message, denoting no client

server performs the following checks: If the ClientEncryptedSNI.key_share group does not match one in <del> the ESNIConfig.keys, it MUST abort the connection with an </del> <ins> the ESNIConfigContents.keys, it MUST abort the connection with an </ins> "illegal_parameter" alert. If the length of the "encrypted_server_name" extension is

abort the connection with a "decrypt_error" alert. If the decrypted value's length is different from the advertised <del> ESNIConfig.padded_length or the padding consists of any value other than 0, then the server MUST abort the connection with an </del> <ins> ESNIConfigContents.padded_length or the padding consists of any value other than 0, then the server MUST abort the connection with an </ins> "illegal_parameter" alert.  Otherwise, the server uses the PaddedServerNameList.sni value as if it were the "server_name" extension.  Any actual "server_name" extension is ignored, which also