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

Reply via email to