あどけない話

Internet technologies

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)