この章ではRFC特有のデータ構造の表現方法と送受信データの構造について説明します。
ネットワークバイトオーダー
データブロックのサイズは1byte単位です。
TLSはネットワークのプロトコルなのでネットワークバイトオーダーです。つまり、複数バイトはビッグエンディアンのバイト順です。
例えばバイト列 01 02
を整数に変換するときは、$((01 \ll 8) + 02) = 256 + 2 = 258$ となります。
value = (byte[0] << 8*(n-1)) | (byte[1] << 8*(n-2)) | ... | byte[n-1];
表記方法
-
コメント :
/*
で始まり*/
で終わります。 -
オプション : 任意の項目は
[[ ]]
(二重角括弧) で囲みます。 - opaque : opaque型はバイト列を持つ型です。
-
エイリアス (別名) : 既存の型Tを使って別名の型T'は
T T';
と定義します。
数値型
基本的な数値データ型は、符号なしバイト (uint8) です。 また、16, 24, 32, 64ビットの数値は、連続した固定長のバイト列から構成され、次のように定義されます。
uint8 uint16[2];
uint8 uint24[3];
uint8 uint32[4];
uint8 uint64[8];
ベクトル型
型Tの固定長ベクトルである新しい型T'は [ ] を使って T T'[n];
と定義します。
ただし、nは型T'が必要なバイト数で、必ず型Tのサイズの倍数になります。
次の例は、3byteのバイト列 (opaque) を持つDatum型と、3つのDatumを持つ合計で9バイトのData型を定義しています。 プログラミング言語の配列とは異なる点に注意してください。
opaque Datum[3]; // 合計で3byteのDatum型
Datum Data[9]; // 合計で9byteのData型
型Tの可変長ベクトルである新しい型T'は < > を使って T'<a..b>
と定義します。
opaque mandatory<3..10>;
型mandatoryは3〜10byteの内容を持つデータで、データ長は最大10なのでデータ長フィールドはuint8を使います。
データ長フィールドの型は、データ長が最大255のときuint8、最大65535のときuint16、最大4294967295のときuint32、… となります。
上記の例で、mandatory型のデータが 01 02 03 04 05
のとき、データ長フィールドを先頭に加えた 05 01 02 03 04 05
が実際のバイト列となります。
列挙型
列挙型は同じタイプの列挙をするときに使います。 次の列挙型Teでは、Te.e1 の値が v1、Te.e2 の値が v2、…となっています。 また、最後の要素名がない (n) だけの部分は、その列挙の最大値を表します。 列挙の要素が使用するバイト数は、列挙の要素の最大値のを表すために必要なバイト数になります。
enum { e1(v1), e2(v2), ... , en(vn) [[, (n)]] } Te;
次の例は、最大値が7のためuint8の列挙Colorと、最大値が32000のためuint16の列挙Tasteを定義したものです。 次の例でバイト列に変換するときの例として Color.red は 03、Taste.bitter は 00 04 となります。
enum { red(3), blue(5), white(7) } Color;
enum { sweet(1), sour(2), bitter(4), (32000) } Taste;
構造体
構造体は複数の型を格納できる型で、C言語の構文と同じように定義します。
struct {
T1 f1;
T2 f2;
...
Tn fn;
} T;
構造体のフィールドは常に固定値(定数)の場合は「=」を使って固定値を代入します。
struct {
T1 f1 = 8; /* T.f1 は常に固定値 8 */
T2 f2;
} T;
TLS 1.3プロトコルで使われる構造体によって、構造体のフィールドの値によって構造体の後続のフィールドの構造が変わるときや、処理者がクライアントかサーバかで構造体の構造が変わる場合があります。 その場合は select case で条件ごとの構造を定義します。
enum { apple(0), orange(1) } VariantTag;
struct { uint16 number; opaque string<0..10>; } V1;
struct { uint32 number; opaque string[10]; } V2;
struct {
VariantTag type;
select (VariantRecord.type) {
case apple: V1;
case orange: V2;
};
} VariantRecord;