On Jul 25, 2017, at 01:29, Kazu Yamamoto (山本和彦) <k...@iij.ad.jp> wrote:
>> 3. Change the definition of `select`'s `case` statements to have 0 or more >> fields (types and names) and remove the optional label. >> 4. Change the `select` example to match the new definition. > > I agree if this means 1 or more. (See below) I went with 0 precisely to avoid Empty, but 1 or more seems fine too. >> 5. Change `Handshake` by adding field names to each `case` statement. (These >> could all be `body` or they could be unique.) > > Would you show me a concrete definition? I give three straw men proposals below, proposal I shows this. >> 6. Delete the `Empty` structure and replace both current uses with the >> comment `/* Empty */`. > > Empty has a long story. > [...] > So, people would want to avoid removing Empty again. Okay, I wasn't aware of this discussion. There's no real issue here except that the two `Empty` structures would need to be given field names. See the proposal I below. Here are three proposals and the impact on the structures containing `select`. Proposal I. Case statements contain 1 or more fields. struct { HandshakeType msg_type; /* handshake type */ uint24 length; /* bytes in message */ select (Handshake.msg_type) { case client_hello: ClientHello ch; case server_hello: ServerHello sh; case end_of_early_data: EndOfEarlyData eed; case hello_retry_request: HelloRetryRequest hrr; case encrypted_extensions: EncryptedExtensions ee; case certificate_request: CertificateRequest cr; case certificate: Certificate c; case certificate_verify: CertificateVerify cv; case finished: Finished f; case new_session_ticket: NewSessionTicket nst; case key_update: KeyUpdate ku; }; } Handshake; struct { select (Handshake.msg_type) { case client_hello: KeyShareEntry client_shares<0..2^16-1>; case hello_retry_request: NamedGroup selected_group; case server_hello: KeyShareEntry server_share; }; } KeyShare; struct {} Empty; struct { select (Handshake.msg_type) { case new_session_ticket: uint32 max_early_data_size; case client_hello: Empty empty; case encrypted_extensions: Empty empty; }; } EarlyDataIndication; struct { select (Handshake.msg_type) { case client_hello: PskIdentity identities<7..2^16-1>; PskBinderEntry binders<33..2^16-1>; case server_hello: uint16 selected_identity; }; } PreSharedKeyExtension; struct { select (certificate_type){ case RawPublicKey: // From RFC 7250 ASN.1_subjectPublicKeyInfo opaque ASN1_subjectPublicKeyInfo<1..2^24-1>; case X509: opaque cert_data<1..2^24-1>; }; Extension extensions<0..2^16-1>; } CertificateEntry; In `Handshake`, the field names themselves are unimportant since they're never referenced. Similarly, the `empty` names aren't important. I find this mildly unpleasant. This involves the fewest changes to the actual structure definitions. Proposal II. Case statements contain a type. Here, fields are replaced by anonymous structs. struct { HandshakeType msg_type; /* handshake type */ uint24 length; /* bytes in message */ select (Handshake.msg_type) { case client_hello: ClientHello; case server_hello: ServerHello; case end_of_early_data: EndOfEarlyData; case hello_retry_request: HelloRetryRequest; case encrypted_extensions: EncryptedExtensions; case certificate_request: CertificateRequest; case certificate: Certificate; case certificate_verify: CertificateVerify; case finished: Finished; case new_session_ticket: NewSessionTicket; case key_update: KeyUpdate; }; } Handshake; struct { select (Handshake.msg_type) { case client_hello: struct { KeyShareEntry client_shares<0..2^16-1>; }; case hello_retry_request: struct { NamedGroup selected_group; }; case server_hello: struct { KeyShareEntry server_share; }; }; } KeyShare; struct {} Empty; struct { select (Handshake.msg_type) { case new_session_ticket: struct { uint32 max_early_data_size; }; case client_hello: Empty; case encrypted_extensions: Empty; }; } EarlyDataIndication; struct { select (Handshake.msg_type) { case client_hello: struct { PskIdentity identities<7..2^16-1>; PskBinderEntry binders<33..2^16-1>; }; case server_hello: struct { uint16 selected_identity; }; }; } PreSharedKeyExtension; struct { select (certificate_type){ case RawPublicKey: struct { // From RFC 7250 ASN.1_subjectPublicKeyInfo opaque ASN1_subjectPublicKeyInfo<1..2^24-1>; }; case X509: struct { opaque cert_data<1..2^24-1>; }; }; Extension extensions<0..2^16-1>; } CertificateEntry; This involves no changes to the presentation language but inserts a bunch of otherwise useless `structs`s and braces. Those that have just a single field could be replaced by the field's type, but this is almost certainly more confusing and loses the field's name. Proposal III. Case statements contain a structure type's name. struct { HandshakeType msg_type; /* handshake type */ uint24 length; /* bytes in message */ select (Handshake.msg_type) { case client_hello: ClientHello; case server_hello: ServerHello; case end_of_early_data: EndOfEarlyData; case hello_retry_request: HelloRetryRequest; case encrypted_extensions: EncryptedExtensions; case certificate_request: CertificateRequest; case certificate: Certificate; case certificate_verify: CertificateVerify; case finished: Finished; case new_session_ticket: NewSessionTicket; case key_update: KeyUpdate; }; } Handshake; struct { KeyShareEntry client_shares<0..2^16-1>; } ClientKeyShares; struct { NamedGroup selected_group; } HelloRetryRequestGroup; struct { KeyShareEntry server_share; } ServerKeyShare; struct { select (Handshake.msg_type) { case client_hello: ClientKeyShares; case hello_retry_request: HelloRetryRequestGroup; case server_hello: ServerKeyShare; }; } KeyShare; struct {} Empty; struct { uint32 max_early_data_size; } MaxEarlyDataSize; struct { select (Handshake.msg_type) { case new_session_ticket: MaxEarlyDataSize; case client_hello: Empty; case encrypted_extensions: Empty; }; } EarlyDataIndication; struct { PskIdentity identities<7..2^16-1>; PskBinderEntry binders<33..2^16-1>; } OfferedIdentities; struct { uint16 selected_identity; } SelectedIdentity; struct { select (Handshake.msg_type) { case client_hello: OfferedIdentities; case server_hello: SelectedIdentity; }; } PreSharedKeyExtension; struct { // From RFC 7250 ASN.1_subjectPublicKeyInfo opaque ASN1_subjectPublicKeyInfo<1..2^24-1>; } SubjectPublicKeyInfo; struct { opaque cert_data<1..2^24-1>; } CertData; struct { select (certificate_type){ case RawPublicKey: SubjectPublicKeyInfo; case X509: CertData; }; Extension extensions<0..2^16-1>; } CertificateEntry; This creates a bunch of auxiliary, named structures to hold the field(s). The `ServerKeyShare` struct could be replaced with `KeyShareEntry`, but this loses the field name. Having written these all out, I like proposal II the least. Proposals I and III have the advantage that anonymous structures can be removed from the presentation language whereas proposal II introduces the only use of them. Initially, I thought proposal I would be the best, but now I have a mild preference for proposal III. I think it makes the language simpler over all. It essentially becomes two primitive types (`opaque` and `uint8`) and four type definitions: 1. Fixed-length vectors: `T1 T[v];` (numbers are a special case where `T1` is `uint8`); 2. Variable-length vectors: `T1 T<v1..v2>;`; 3. Enumerated types: `enum { e1(r1), ..., en(rn) [[, (vm)]] } T;`; and 4. Structures: struct { F1; F2; ... Fm; [[ select (Ts.fs) { case e1: S1; case e2: S2; ... case en: Sn; }; ]] } T; Each field Fi is one of - `Ti fi` (normal fields); - `Ti fi = v` (constants where `Ti` is a number type); - `Ti fi[v]` (fixed-length vectors); or - `Ti fi<v1..v2>` (variable-length vectors). where the symbols have the following meanings: - S: structure type identifier - T: type identifier - e: enum element - f: field identifier - r: enum value, either a number or a range `v1..v2` - v: number Note that currently, the presentation language doesn't describe fixed- nor variable-length vectors as _structure fields_, it only describes them in the context of defining new types. Neither the current presentation language nor my proposed one above gives meaning to `uint16 ProtocolVersion;`. Absent a compelling reason for type aliases, I think this should be written as `uint8 ProtocolVersion[2];` -- Stephen Checkoway _______________________________________________ TLS mailing list TLS@ietf.org https://www.ietf.org/mailman/listinfo/tls