TLSの符号化
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)