TLSのデータがどう符号化(シリアライズ)されるかのメモ。簡単にいうと可変長配列だけ先頭に「長さ」が付き、それ以外はそのまま。
基本型
たとえば、ProtocolVersionは uint16 と定義されている。
uint16 ProtocolVersion;
TLSのバージョン1.3の値は、0x0304であり、そのままネットワークバイトオーダー(ビッグエンディアン)で符号化される。
03 04 # TLS 1.3
固定長配列
固定長配列は、角括弧を使って表す。
たとえば、CipherSuiteが以下のように2バイトで定義されている。
uint8 CipherSuite[2];
TLS_AES_256_GCM_SHA384 は 0x1302 と定義されている。
13 02 # TLS_AES_256_GCM_SHA384
可変長配列
可変長配列は、小なり大なりで表す。小なり大なりの中に、長さの最大値が指定されるので、その分の長さを最初に付ける。
CipherSuiteの可変長配列が、後述の構造体の中で、以下のように定義されているとしよう。
CipherSuite cipher_suites<2..2^16-2>;
cipher_suitesはフィールド名である。216 とあるので、長さは2バイトだと分かる。
00 06 # 以下6バイト 13 02 # TLS_AES_256_GCM_SHA384 13 01 # TLS_AES_128_GCM_SHA256 13 03 # TLS_CHACHA20_POLY1305_SHA256
構造体
構造体は struct を使って表す。符号化は、単に上から順に書き出せばよい。
たとえば、ClinetHello は以下のように定義されている。
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;
opaqueの定義はどこにもないけれど、単なるバイトだと理解する。Extension の定義はこう。
struct { ExtensionType extension_type; opaque extension_data<0..2^16-1>; } Extension;
実際のバイト列と照らし合わせてみる。
03 03 # ProtocolVersion: TLS v1.2 96 f7 37 2d 59 66 76 40 68 90 f8 d2 f5 b0 e1 b1 # Random 4b ac 3a ed 7a 25 ab e0 d0 f5 22 15 64 52 51 7f 20 # Session Id Length 95 f8 f0 4d 5c c1 df 3d 72 70 8f c9 28 37 40 eb # Session Id d0 13 a5 8d 06 fe f2 5e bf 1e 8e 55 fb 70 9a 59 00 06 # Cipher Suites Length 13 02 # Cipher Suite (TLS_AES_256_GCM_SHA384) 13 01 # Cipher Suite (TLS_AES_128_GCM_SHA256) 13 03 # Cipher Suite (TLS_CHACHA20_POLY1305_SHA256) 01 # Compression Methods Length 00 # Compression Method: (null) 00 c4 # Extensions Length 00 33 # ExtensionType (key_share) 00 47 # Extension Length ... 00 0b # ExtensionType (supported_versions) 00 09 # Extension Length ... 00 0d # ExtensionType (signature_algorithms) 00 0a # Extension Length ... 00 0a # ExtensionType (supported_groups) 00 04 # Extension Length ... 00 2d # ExtensionType (psk_key_exchange_modes) 00 03 # Extension Length ... 00 29 # ExtensionType (pre_shared_key) 00 4b # Extension Length ...
supported_versions の定義はこう:
struct { select (Handshake.msg_type) { case client_hello: ProtocolVersion versions<2..254>; case server_hello: /* and HelloRetryRequest */ ProtocolVersion selected_version; }; } SupportedVersions;
select があってごちゃごちゃしているが、client_hello用に書き直すとこう。
struct { ProtocolVersion versions<2..254>; } SupportedVersions;
これが Extension の opaque extension_data の部分となる。versionsは、可変長配列だから、長さがだぶったような感じに符号化される。
00 0b # ExtensionType (supported_versions) 再掲 00 09 # Extension Length 再掲 08 # Supported Versions Length 03 04 # TLS 1.3 (0x0304) 7f 1c # TLS 1.3 (draft 28) 7f 1b # TLS 1.3 (draft 27) 7f 1a # TLS 1.3 (draft 26)