RFC9001のA.2. Client Initial にあるCRYPTOフレームのサンプルを読み解いてみる

RFC9001 A.2. Client Initial にある以下のCRYPTO Frameを読み解いてみます。

060040f1010000ed0303ebf8fa56f129 39b9584a3896472ec40bb863cfd3e868
04fe3a47f06a2b69484c000004130113 02010000c000000010000e00000b6578
616d706c652e636f6dff01000100000a 00080006001d00170018001000070005
04616c706e0005000501000000000033 00260024001d00209370b2c9caa47fba
baf4559fedba753de171fa71f50f1ce1 5d43e994ec74d748002b000302030400
0d0010000e0403050306030203080408 050806002d00020101001c0002400100
3900320408ffffffffffffffff050480 00ffff07048000ffff08011001048000
75300901100f088394c8f03e51570806 048000ffff

先に結果を書くと、以下のようになります。

CRYPTOフレームの定義があります。そのあとに、TLS1.3のClientHelloが含まれます。ClientHelloの拡張の中には、QUIC Transport Paramter 拡張も含まれています。

060040f1: CRYPTOフレームの定義
010000ed: Handshakeの定義
0303ebf8fa56f129 39b9584a3896472ec40bb863cfd3e868: 
0303ebf8fa56f129 39b9584a3896472ec40bb863cfd3e86804fe3a47f06a2b69484c00000413011302010000c0 : Client Helloの定義(extensionの長さを示す部分まで)
00000010000e00000b6578616d706c652e636f6d: server_name拡張の定義
ff01000100:  Renegotiation Indication 拡張
000a00080006001d00170018: supported group 拡張
00100007000504616c706e: ALPN拡張
000500050100000000: status request 拡張
003300260024001d00209370b2c9caa47fbabaf4559fedba753de171fa71f50f1ce1 5d43e994ec74d748: key share拡張
002b0003020304: supported versions拡張
000d0010000e0403050306030203080408050806: signature algorithms 拡張
002d00020101:  Pre-Shared Key Exchange Modes 拡張
001c00024001: Record Size Limit 拡張
003900320408ffffffffffffffff05048000ffff07048000ffff0801100104800075300901100f088394c8f03e51570806048000ffff: QUIC Transport Parameter 拡張

CRYPTO フレームを読む。

まずは、CRYPTOフレームの定義の確認です。

CRYPTOフレームの定義は、RFC9000の19.6. CRYPTO Frames で以下のように定義されています。

   CRYPTO Frame {
     Type (i) = 0x06,
     Offset (i),
     Length (i),
     Crypto Data (..),
   }

ここで、OffsetとLengthは、variable-length integerとなっているので注意が必要です。

variable-length integer encodingは、RFC9000の16. Variable-Length Integer Encoding で定義されています。

MSBの2ビットから、整数の長さを表すのに何バイトが使われているかがわかります。

バイナリデータの0x06は、CRYPTOフレームであることを表します。

次の値は0です。MSBは0b00となるので、offsetは長さ1バイトということがわかります。

また、MSBを除いた6ビットは、0b00'0000 となっているため、offsetの値は0であることがわかります。

次のバイトは0x40です。0x40 = 0b0100'0000 とあらわすことが出来ます。MSBの2ビットが0b01なので、6ビット分の0b00'11110001 がCrypto dataの長さを表しています。これは 241になります。

クライアントが送信するデータなのでCrypto dataはClient Helloになります。

ここで解析したバイナリ

0x060040f1

Handshakeを読む

いきなりClient Helloが格納されているわけではありません。まず最初にRFC8446 (TLS1.3) で定義されているHandshake メッセージ 4. Handshake Protocol が格納されます。

Handshakeメッセージは以下のように定義されています。

      enum {
          client_hello(1),
          server_hello(2),
         ...
          message_hash(254),
          (255)
      } HandshakeType;

      struct {
          HandshakeType msg_type;    /* handshake type */
          uint24 length;             /* remaining bytes in message */
          select (Handshake.msg_type) {
              case client_hello:          ClientHello;
              case server_hello:          ServerHello;
              ...
          };
      } Handshake;

Handshake の定義を見ると、まずは、HansdshakeType msg_typeがあります。

msg_type は8ビットの整数です。まだ読み込んでいない部分は0x01 です。

Handshake Typeの定義を見ると、0x01client_helloとなっています。サンプルの想定通り、このメッセージがClientHelloであることがわかります。

Handshakeの次の3バイトはlengthを表しています。0x0000ed は237です。先ほどCrypto dataが241バイトであると読んだところから4バイト読み込んでいるので矛盾していないことがわかります。

msg_type0x0なので、次の selectは、case ClientHello を選んで解析します。

ここで解析したバイナリ

0x010000ed

ClientHelloを読む

Client Hello はRFC-8446の 4.1.2. Client Hello で定義されています。

ClientHelloの定義は、以下のようになっています。legacy_versionlegacy_compression_methods までは含まれるものが決まっており、そのあとにTLS拡張が含まれるフォーマットになっています。

      uint16 ProtocolVersion;
      opaque Random[32];

      uint8 CipherSuite[2];    /* Cryptographic suite selector */

      struct {
          ProtocolVersion legacy_version = 0x0303;    /* TLS v1.2 */
          Random random;
          opaque legacy_session_id<0..32>;
          CipherSuite cipher_suites<2..2^16-2>;
          opaque legacy_compression_methods<1..2^8-1>;
          Extension extensions<8..2^16-1>;
      } ClientHello;

まずは最初に legacy_version を表す 0x0303があります。

次に、32バイトのrandomデータがあります。バイナリ上の、0xebf8fa56f12939b9584a3896472ec40bb863cfd3e86804fe3a47f06a2b69484c の部分です。

次に可変長の legacy_session_id<0..32> があります。legacy_session_idは長さ0~32の可変長のバイト列です。このような可変長のデータがある場合、最初に長さが格納され、そのあとに実際のデータが格納されています。

また、長さを表す部分は、8の倍数でありうる最大長を表現できるようなバイト数を使います。この場合は非負整数なので8ビットで表せる範囲です。そのため、長さには8ビット=1バイトが使われます。実際のデータを見ると、ここは長さが0x00なので、長さ0であることが分かります。legacy_versionは長さ0のデータです。

次は cipher_suites です。ここにはクライアントがサポートする暗号スイートのリストが格納されます。ここでも最初に長さを読み込みます。このリストは最大 216-2 なので、16ビットのデータを読みます。データは0x0004 なので4バイトであることが分かります。

TLS1.3で定義されている暗号スイートは、RFC8446のB.4. Cipher Suites で確認できます。

2バイトずつ読み込むと、0x13010x1302 となっており、それぞれ TLS_AES_128_GCM_SHA256TLS_AES_256_GCM_SHA384 であることがわかります。

同じように legacy_compression_methods は、長さ1バイトで値は0であることがわかります。

最後はextensionsです。extension の長さは 0x00c0 なので192バイトであること分かります。

ここで解析したバイナリ

0x0303ebf8fa56f129 39b9584a3896472ec40bb863cfd3e86804fe3a47f06a2b69484c00000413011302010000c0

TLS拡張を読む

TLS拡張は、RFC8446の4.2. ExtensionsTLSに関連したRFCで定義されているTLS拡張や、QUIC用に定義されている Transport Parameter Extension QUIC Transport Parameters Extension があります。

RFC8446で、TLS拡張は以下のように定義されています。

struct {
        ExtensionType extension_type;
        opaque extension_data<0..2^16-1>;
    } Extension;

    enum {
        server_name(0),                             /* [RFC 6066](https://www.rfc-editor.org/rfc/rfc6066) */
        max_fragment_length(1),                     /* [RFC 6066](https://www.rfc-editor.org/rfc/rfc6066) */
        status_request(5),                          /* [RFC 6066](https://www.rfc-editor.org/rfc/rfc6066) */
        supported_groups(10),                       /* [RFC 8422](https://www.rfc-editor.org/rfc/rfc8422), 7919 */
        signature_algorithms(13),                   /* [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) */
        use_srtp(14),                               /* [RFC 5764](https://www.rfc-editor.org/rfc/rfc5764) */
        heartbeat(15),                              /* [RFC 6520](https://www.rfc-editor.org/rfc/rfc6520) */
        application_layer_protocol_negotiation(16), /* [RFC 7301](https://www.rfc-editor.org/rfc/rfc7301) */
        signed_certificate_timestamp(18),           /* [RFC 6962](https://www.rfc-editor.org/rfc/rfc6962) */
        client_certificate_type(19),                /* [RFC 7250](https://www.rfc-editor.org/rfc/rfc7250) */
        server_certificate_type(20),                /* [RFC 7250](https://www.rfc-editor.org/rfc/rfc7250) */
        padding(21),                                /* [RFC 7685](https://www.rfc-editor.org/rfc/rfc7685) */
        pre_shared_key(41),                         /* [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) */
        early_data(42),                             /* [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) */
        supported_versions(43),                     /* [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) */
        cookie(44),                                 /* [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) */
        psk_key_exchange_modes(45),                 /* [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) */
        certificate_authorities(47),                /* [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) */
        oid_filters(48),                            /* [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) */
        post_handshake_auth(49),                    /* [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) */
        signature_algorithms_cert(50),              /* [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) */
        key_share(51),                              /* [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) */
        (65535)
    } ExtensionType;

最初にTLS拡張全体の長さを表す16ビットの情報があります。そのあとにそのTLS拡張を表す可変長のバイト列が含まれます。

ここからはTLS拡張を一つずつ読み解いていきます。

Server Name 拡張

一つ目のTLS拡張は0x0000 で表されるServer Name 拡張です。この拡張の長さは 0x0010 から16バイトであることがわかります。

Server Name 拡張は、RFC6066の 3. Server Name Indication で以下のように定義されています。

      struct {
          NameType name_type;
          select (name_type) {
              case host_name: HostName;
          } name;
      } ServerName;

      enum {
          host_name(0), (255)
      } NameType;

      opaque HostName<1..2^16-1>;

      struct {
          ServerName server_name_list<1..2^16-1>
      } ServerNameList;

ここにはServerNameListが格納されています。server_name_list は16ビットで長さを表しています。データを見ると 0x000e となっており、長さが14であることがわかります。

次に、server_name_list に含まれる ServerNameの中身を見ていきます。

まずは、name_type です。これは 0x00host_nameみが定義されており、実際にデータも0x00 となっていることがわかります。

name_typehost_nameの場合、次にHostName が含まれます。これは長さが16ビットで表される可変長のバイト列です。

最初に長さを見ると、0x000b なので11バイトであることがわかります。

この後に続く11バイトは、65 78 61 6d 70 6c 65 2e 63 6f 6d となっています。これはexample.com を表しています。

ここで解析したバイナリ

0x00000010000e00000b6578616d706c652e636f6d

Renegotiation Indication拡張

次の2バイトは、0xff01 です。これはRenegotiation Indication拡張を表します。この拡張の長さは0x0001 なので1バイトです。

これは、TLS1.3では廃止されているRFC5746で定義されているRenegotiation Indication拡張を表します。サンプルなので入っているのだと思います。ここでは、特に気にせず読みといておきます。

この拡張の定義は、3.2. Extension Definition に書かれています。

      struct {
          opaque renegotiated_connection<0..255>;
      } RenegotiationInfo;

なので1バイトを読み込みます。0x00 なので、長さ0のデータがあることを表します。

ここで解析したバイナリ

0xff01000100

supported_groups 拡張

次の2バイトは0x000a です。これはsupported_groups 拡張を表します。この拡張の長さは0x0008 なので8バイトです。

この拡張はRFC8422の5.1.1. Supported Elliptic Curves Extension.で定義されています。

           enum {
               deprecated(1..22),
               secp256r1 (23), secp384r1 (24), secp521r1 (25),
               x25519(29), x448(30),
               reserved (0xFE00..0xFEFF),
               deprecated(0xFF01..0xFF02),
               (0xFFFF)
           } NamedCurve;
           struct {
               NamedCurve named_curve_list<2..2^16-1>
           } NamedCurveList;

named_curve_list の長さは0x0006 なので6バイトです。

この後に続く6バイトが、2バイトのNamedCurve3つを表しています。

0x001d, 0x0017, 0x0018 がそれぞれ、29, 23, 24 を表しています。

NamedCurveの定義から、 x25519、secp256r1、secp384r1 に該当していることがわかります。

ここで解析したバイナリ

0x000a00080006001d00170018

ALPN (application layer protocol negotiation) 拡張

次の2バイトは、0x0010 です。これはALPN拡張を表します。この拡張の長さは0x0007 なので7バイトです。

ALPN拡張はRFC7301の3.1. The Application-Layer Protocol Negotiation Extension で定義されています。

ALPN拡張は以下のように定義されています。

   opaque ProtocolName<1..2^8-1>;

   struct {
       ProtocolName protocol_name_list<2..2^16-1>
   } ProtocolNameList;

protocol_name_list0x0005 です。ProtocolName は16ビットで長さがあらわされるバイト列です。

ここでは長さが0x04 で、中身の4バイトは、61 6c 70 6e となっています。これは alpn と書かれています。

ここで解析したバイナリ

00100007000504616c706e

Status Request 拡張

次の2バイトは、0x0005 です。これはStatus Request 拡張を表します。この拡張の長さは0x0005 なので7バイトです。

Status Request 拡張はRFC6066の 8. Certificate Status Request に記述されています。

struct {
  CertificateStatusType status_type;
  select (status_type) {
      case ocsp: OCSPStatusRequest;
  } request;
} CertificateStatusRequest;

enum { ocsp(1), (255) } CertificateStatusType;

struct {
  ResponderID responder_id_list<0..2^16-1>;
  Extensions  request_extensions;
} OCSPStatusRequest;

opaque ResponderID<1..2^16-1>;
opaque Extensions<0..2^16-1>;

CertificateStatusRequestのstatus_typeは8ビットで表される0x01 なので、oscp になります。oscpの場合OCSPStatusRequestが含まれます。OCSPStatusRequest の最初の要素はresponder_id_list です。これは長さが16ビットで表される可変長のバイト列です。ここでは長さが 0x0000なので長さ0の空のバイト列になります。

Extensionsも長さが16ビットで表される可変長のバイト列です。ここも長さが0x00なので長さ0の空のバイト列になります。

ここで解析したバイナリ

0x000500050100000000

Key Share 拡張

次の2バイトは、0x0033 です。これはKey Share拡張を表します。この拡張の長さは0x0026 なので38バイトです。

Key Share拡張はRFC8446の4.2.8. Key Shareで定義されています。

Client Helloの場合は、KeyShareClientHello という構造が含まれます。

      struct {
          KeyShareEntry client_shares<0..2^16-1>;
      } KeyShareClientHello;

      struct {
          NamedGroup group;
          opaque key_exchange<1..2^16-1>;
      } KeyShareEntry;

      enum {
          unallocated_RESERVED(0x0000),

          /* Elliptic Curve Groups (ECDHE) */
          obsolete_RESERVED(0x0001..0x0016),
          secp256r1(0x0017), secp384r1(0x0018), secp521r1(0x0019),
          obsolete_RESERVED(0x001A..0x001C),
          x25519(0x001D), x448(0x001E),

          /* Finite Field Groups (DHE) */
          ffdhe2048(0x0100), ffdhe3072(0x0101), ffdhe4096(0x0102),
          ffdhe6144(0x0103), ffdhe8192(0x0104),

          /* Reserved Code Points */
          ffdhe_private_use(0x01FC..0x01FF),
          ecdhe_private_use(0xFE00..0xFEFF),
          obsolete_RESERVED(0xFF01..0xFF02),
          (0xFFFF)
      } NamedGroup;

KeyShareClientHello の最初に含まれる、client_sharesの長さは、0x0024なので36バイトになります。

次にKeyShareEntryが含まれます。この先頭には、NamedGroup が含まれています。この場合は0x001d です。なので、x25519(0x001D)であることがわかります。

続く key_exchange の長さは、0x0020 = 32バイトです。なので、これ以降の32バイト 0x9370b2c9caa47fbabaf4559fedba753de171fa71f50f1ce15d43e994ec74d748key_exchange の中身になります。

この辺りは専門ではないのでわかりませんでしたが、RFC7748の5 あたりを読むとわかるかもしれません。

ここで解析したバイナリ

0x003300260024001d00209370b2c9caa47fbabaf4559fedba753de171fa71f50f1ce1 5d43e994ec74d748

Supported Versions 拡張

次の2バイトは、0x002b です。これはSupported Versions拡張を表します。この拡張の長さは0x0003 なので3バイトです。

これは、RFC8446の4.2.1. Supported Versions で定義されている Supported Version拡張です。

      struct {
          select (Handshake.msg_type) {
              case client_hello:
                   ProtocolVersion versions<2..254>;

              case server_hello: /* and HelloRetryRequest */
                   ProtocolVersion selected_version;
          };
      } SupportedVersions;

ClientHelloの場合は、protocol_versionが格納されます。

この場合、protocol_versionの長さは0x02なので1バイト、中身は、0x0304 です。

ここで解析したバイナリ

0x002b0003020304

Signature Algorithms拡張

次の2バイトは、000d です。これはSignature Algorithms拡張を表します。この拡張の長さは0x0010 なので16バイトです。

Signature Algorithms拡張はRFC8446の4.2.3. Signature Algorithms で定義されています。

ここに含まれるものは、SignatureSchemeList で定義されています。

      enum {
          /* RSASSA-PKCS1-v1_5 algorithms */
          rsa_pkcs1_sha256(0x0401),
          rsa_pkcs1_sha384(0x0501),
          rsa_pkcs1_sha512(0x0601),

          /* ECDSA algorithms */
          ecdsa_secp256r1_sha256(0x0403),
          ecdsa_secp384r1_sha384(0x0503),
          ecdsa_secp521r1_sha512(0x0603),

          /* RSASSA-PSS algorithms with public key OID rsaEncryption */
          rsa_pss_rsae_sha256(0x0804),
          rsa_pss_rsae_sha384(0x0805),
          rsa_pss_rsae_sha512(0x0806),

          /* EdDSA algorithms */
          ed25519(0x0807),
          ed448(0x0808),

          /* RSASSA-PSS algorithms with public key OID RSASSA-PSS */
          rsa_pss_pss_sha256(0x0809),
          rsa_pss_pss_sha384(0x080a),
          rsa_pss_pss_sha512(0x080b),

          /* Legacy algorithms */
          rsa_pkcs1_sha1(0x0201),
          ecdsa_sha1(0x0203),

          /* Reserved Code Points */
          private_use(0xFE00..0xFFFF),
          (0xFFFF)
      } SignatureScheme;

      struct {
          SignatureScheme supported_signature_algorithms<2..2^16-2>;
      } SignatureSchemeList;

SignatureSchemeList が格納されています。

supported_signature_algorithms の長さは、0x000e なので14バイトです。

SignatureScheme は16ビットで表されるデータです。14バイトのバイナリには、7つのSignatureShemeが書かれています。

具体的には、以下の7つです。

  • 0403: ecdsa_secp256r1_sha256(0x0403),
  • 0503: ecdsa_secp384r1_sha384(0x0503),
  • 0603: ecdsa_secp521r1_sha512(0x0603),
  • 0203: ecdsa_sha1(0x0203),
  • 0804: rsa_pss_rsae_sha256(0x0804),
  • 0805: rsa_pss_rsae_sha384(0x0805),
  • 0806: rsa_pss_rsae_sha512(0x0806),
ここで解析したバイナリ

0x000d0010000e0403050306030203080408050806

Pre-Shared Key Exchange Modes 拡張

次の2バイトは、0x002d です。これはPre-Shared Key Exchange Modes拡張を表します。この拡張の長さは0x0002 なので2バイトです。

Pre-Shared Key Exchange Modes拡張は、RFC8446の4.2.9. Pre-Shared Key Exchange Modesで定義されています。

この構造は以下のように定義されています。

      enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;

      struct {
          PskKeyExchangeMode ke_modes<1..255>;
      } PskKeyExchangeModes;

ここでは、ke_modesの長さは0x01で1バイト、中身は、0x01 なので psk_dhe_ke が定義されています。

ここで解析したバイナリ

0x002d00020101

Record Size Limit 拡張

次の2バイトは、001c です。これはRecord Size Limit 拡張を表します。この拡張の長さは0x0002 なので2バイトです。

これはおそらく、RFC8449の4. The "record_size_limit" Extension で定義されているRecord Size Limit 拡張です。

uint16 RecordSizeLimit;

ここでは16ビットを使用した 0x4001=16385 となります。

ここで解析したバイナリ

0x001c00024001

QUIC Transport Parameters 拡張

次の2バイトは、0x0039 です。これはQUIC Transport Parameters 拡張を表します。この拡張の長さは0x0032 なので2バイトです。

QUIC Transport Parameters 拡張は、RFC9001の8.2. QUIC Transport Parameters Extension で定義されてます。

具体的には、以下のように定義されています。

   enum {
      quic_transport_parameters(0x39), (65535)
   } ExtensionType;

ここに含まれるTransport Parameter はRFC9000の18. Transport Parameter Encodingで以下のように定義されています。

Transport Parameter {
  Transport Parameter ID (i),
  Transport Parameter Length (i),
  Transport Parameter Value (..),
}

また、Transport ParameterのIDは、RFC9000の18.2. Transport Parameter Definitionsで定義されています。

Transport ParameterのIDとLengthはそれぞれ可変長整数です。

最初のTransport Parameterは、0x04 なので、initial_max_data です。長さは0x08 で、中身は0xffffffffffffffff です。

次は、0x05なので、initial_max_stream_data_bidi_localです。これは長さ0x04で、中身は0x8000ffffです。

次は、0x07 なので、initial_max_stream_data_uniです。これは長さ0x04で、中身は 0x8000ffff です。

次は、0x08 なので、 initial_max_streams_bidiです。これは長さ0x01で、中身は 0x10 です。

次は、0x01 なので、max_idle_timeoutです。これは長さ0x04で、中身は0x80007530 です。

次は、0x09 なので、initial_max_streams_uniです。これは長さ0x01で、中身は0x10です。

次は、0x0fなので、initial_source_connection_idです。これは、長さ0x08で、中身は0x8394c8f03e515708 です。

最後は、0x06なので、initial_max_stream_data_bidi_remoteです。長さは0x04で、中身は0x8000ffff です。

ここで解析したバイナリ

0x003900320408ffffffffffffffff05048000ffff07048000ffff0801100104800075300901100f088394c8f03e51570806048000ffff

まとめ

今回は、RFC9001 A.2. Client Initial にある以下のCRYPTO Frameを読み解いてみました。

RFC9001には、CRYPTOフレーム+PADDINGフレームと、パケットヘッダ―を暗号化したものも書かれています。次はその生成をやってみようと思います。