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
の定義を見ると、0x01
はclient_hello
となっています。サンプルの想定通り、このメッセージがClientHelloであることがわかります。
Handshake
の次の3バイトはlength
を表しています。0x0000ed
は237です。先ほどCrypto dataが241バイトであると読んだところから4バイト読み込んでいるので矛盾していないことがわかります。
msg_type
が0x0
なので、次の select
は、case ClientHello
を選んで解析します。
ここで解析したバイナリ
0x010000ed
ClientHelloを読む
Client Hello はRFC-8446の 4.1.2. Client Hello で定義されています。
ClientHello
の定義は、以下のようになっています。legacy_version
~ legacy_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バイトずつ読み込むと、0x1301
、0x1302
となっており、それぞれ TLS_AES_128_GCM_SHA256
と TLS_AES_256_GCM_SHA384
であることがわかります。
同じように legacy_compression_methods
は、長さ1バイトで値は0であることがわかります。
最後はextensions
です。extension
の長さは 0x00c0
なので192バイトであること分かります。
ここで解析したバイナリ
0x0303ebf8fa56f129 39b9584a3896472ec40bb863cfd3e86804fe3a47f06a2b69484c00000413011302010000c0
TLS拡張を読む
TLS拡張は、RFC8446の4.2. Extensions やTLSに関連した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
です。これは 0x00
のhost_name
みが定義されており、実際にデータも0x00
となっていることがわかります。
name_type
がhost_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_list
は 0x0005
です。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バイト 0x9370b2c9caa47fbabaf4559fedba753de171fa71f50f1ce15d43e994ec74d748
が key_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フレームと、パケットヘッダ―を暗号化したものも書かれています。次はその生成をやってみようと思います。