6. TLS レコードプロトコル English
(準備中)
TLS レコードプロトコルは、複数の層から構成されている。各層のメッセージは、データ長、情報、およびコンテンツ要の各フィールドで構成されている。レコードプロトコルは、上位層から渡されたメッセージを受け取り、データを処理しやすいブロックへ分解し、任意でデータを圧縮し、MAC を適用し、暗号化し、結果を下位レベル層に渡す。また下位レベル層から受け取ったデータを復号し、検証し、展開し、組み立て直して、上位レベルのクライアントに渡す。
4 つのレコードプロトコルクライアントが本書で記述されている。それはハンドシェイクプロトコル、Alert プロトコル、ChangeCipherSpec プロトコル、およびアプリケーションデータプロトコルである。 TLS プロトコルを拡張するために、レコードプロトコルに対して新規のレコードタイプを追加することができるようになっている。新規のレコードタイプは、ここで記述された 4 つのレコードタイプ(付録 A.2 参照)のContentType 値のすぐ上に、自分自身のタイプの値を割り当てる必要がある。TLS 実装においては、理解のできないレコードタイプを受信したならば、単純にそれを無視するべきである。TLS を使用するように設計されるプロトコルは、可能性のあるすべての攻撃に対応できるように入念に設計されなければならない。レコードタイプとデータ長の情報は、暗号化による保護は行われないので、これらのトラフィック分析値を最小にするための注意を払うべきである。
TLS コネクションステータスは、TLS レコードプロトコルの動作環境である。ステータスでは、圧縮アルゴリズム、暗号化アルゴリズム、および MAC アルゴリズムが指定される。さらに、これらのアルゴリズムのパラメータである、MAC シークレット、バルク暗号化鍵、および、読み込みと書き出しの両方のコネクションにおけるIVも知られている。論理的には、常に 4 つのコネクションステータスが存在する。それは、カレントの読み込みと書き出しのステータス、保留中の読み込みと書き出しのステータスである。すべての L コードは、カレントの読み込みと書き出しのステータスの下で処理される。保留ステータスのセキュリティパラメータは、TLS ハンドシェイクプロトコルによって設定することが出来る。そしてハンドシェイクプロトコルはまた、2 つの保留ステータスをカレントステータスに変更することができる。この場合、適切なカレントステータスは廃棄され、保留ステータスで置きかえられる。そして保留ステータスは、空の状態に再初期化される。セキュリティパラメータで初期化されていないステータスをカレントステータスにするのは違反である。カレントステータスの初期状態では、どのような暗号化、圧縮、または MAC も使用されない。
TLS コネクションの読み込みと書き出しのステータスにおけるセキュリティパラメータは、次の値を提供することによって設定される。:
- コネクション端
- このエンティティは、コネクションにおける「クライアント」であるか「サーバー」であるか。
- バルク暗号化アルゴリズム
- バルク暗号化に使用されるアルゴリズム。この仕様には、このアルゴリズムの鍵サイズ、有効鍵長、暗号はブロック型であるかストリーム型であるか、(該当する場合)暗号のブロックサイズ、法令上、「輸出」可能な暗号であるか否かが含まれる。
- MAC アルゴリズム
- メッセージ認証に使用されるアルゴリズム。この仕様には、MAC アルゴリズムによって返されるハッシュサイズが含まれる。
- 圧縮アルゴリズム
- データ圧縮に使用されるアルゴリズム。この仕様には、アルゴリズムが圧縮を実行するのに必要とするすべての情報が含まれていなければならない。
- マスターシークレット
- コネクションの両端で共有される 48バイトのシークレット。
- クライアント乱数
- クライアントによって提供される 32バイト値。
- サーバー乱数
- サーバーによって提供される 32バイト値。
これらのパラメータは、ここで使用する言語を使用して、次のように定義される。
enum { server, client } ConnectionEnd;
enum { null, rc4, rc2, des, 3des, des40 } BulkCipherAlgorithm;
enum { stream, block } CipherType;
enum { true, false } IsExportable;
enum { null, md5, sha } MACAlgorithm;
enum { null(0), (255) } CompressionMethod;
/* CompressionMethod, BulkCipherAlgorithm, そしてMACAlgorithm で指定されるアルゴリズムは、追加が可能である。*/
struct {
ConnectionEnd entity;
BulkCipherAlgorithm bulk_cipher_algorithm;
CipherType cipher_type;
uint8 key_size;
uint8 key_material_length;
IsExportable is_exportable;
MACAlgorithm mac_algorithm;
uint8 hash_size;
CompressionMethod compression_algorithm;
opaque master_secret[48];
opaque client_random[32];
opaque server_random[32];} SecurityParameters;
レコード層では、セキュリティパラメータを使用して、以下の6つを生成する。
クライアント書き出し MAC シークレット
サーバー書き出し MAC シークレット
クライアント書き出し鍵
サーバー書き出し鍵
クライアント書き出し IV (ブロック暗号のみ)
サーバー書き出し IV (ブロック暗号のみ)クライアント書き出しパラメータは、サーバーにおいては、レコードを受信し処理するときに使用される。逆もまた同様である。セキュリティパラメータからこれらを生成するのに使用されるアルゴリズムは、6.3 章で述べる。 いったんセキュリティパラメータが設定され鍵が生成されると、コネクションステータスは、それをカレントステータスにすることにより、開始できる。これらのカレントステータスは、処理される各レコードごとにアップデートされなければならない。それぞれのコネクションステータスは以下の要素を含む。
- 圧縮ステータス
- 圧縮アルゴリズムのカレントステータス。
- 暗号ステータス
- 暗号化アルゴリズムのカレントステータス。これはそのコネクションにスケジュールされた鍵から構成される。さらに、CBC モード(TLS で指定される唯一のモード)で動作するブロック暗号においては、最初はそのコネクションステータスのIVが含まれている。IV はレコードが処理されるに従い、暗号化または復号された最新の暗号ブロックにアップデートされる。ストリーム暗号においては、ここにはデータを暗号化または復号するために必要な、すべてのステータス情報が含まれる。
- MAC シークレット
- 上記のように生成された、このコネクションにおける MAC シークレット。
- シーケンス番号
- それぞれのコネクションステータスには、シーケンス番号が含まれる。読み込み用と書き出し用の番号はそれぞれ別々に維持される。コネクションステータスがアクティブになったときには、シーケンス番号は 0 にセットされなければならない。シーケンス番号は uint64 型であり、2^64-1 を超えない。シーケンス番号は、各レコードごとにインクリメントされる。明らかなことであるが、ある特定のコネクションステータスの下で転送される最初のレコードは、シーケンス番号 0 を使用するべきである。
TLS レコード層は、 上位レベルの層から、任意サイズの空でないブロックデータを受け取る。このデータは解析されない。
レコード層では、情報ブロックを、2^14 バイト以下のチャンクとして、 TLSPlaintext レコードデータに分割する。クライアントメッセージの境界は 、レコード層では保持されない。(たとえば、同じ ContentType を持つ複数のクライアントメッセージが、1つの TLSPlaintext レコードとして結合されたり、1つのメッセージがいくつかのレコードに渡って分割されることがある 。)
struct {
uint8 major, minor;
} ProtocolVersion;
enum {change_cipher_spec(20), alert(21), handshake(22),
application_data(23), (255)} ContentType;
struct {ContentType type;
ProtocolVersion version;
uint16 length;
opaque fragment[TLSPlaintext.length];} TLSPlaintext;
- type
- フラグメントされたデータを処理する上位のプロトコルタイプ。
- version
- 使用されるプロトコルのバージョン。本書においては、TLS バージョン 1.0 について記述しているが、その場合バージョン {3、1} を使用する。バージョン値である 3.1 は歴史的な理由による。TLS バージョン 1.0 は 、SSL3.0 プロトコルをマイナーチェンジしたものである。SSL3.0 では、バージョン値は 3.0 である (付録 A.1 を参照)。
- length
- 次の TLSPlaintext.fragment の長さ(単位バイト)。長さは 2^14 を超えない。
- fragment
- アプリケーションデータ。 このデータは、透過であり、type フィールドで指定されている上位プロトコルによって処理されるよう、独立したブロックとして処理される。
注: 異なる TLS レコード層コンテンツタイプを持つデータが割り込んでいてもよい。一般に、アプリケーションデータは、転送に際しては他のコンテンツタイプよりも優先度が低い。
すべてのレコードは、カレントのセッションステータスで定義されている圧縮アルゴリズムを使用して圧縮される。常に、アクティブ状態の圧縮アルゴリズムが存在する。しかし、初期状態では CompressionMethod.null として定義される。圧縮アルゴリズムにより、TLSPlaintext 構造体は TLSCompressed 構造体に変換される。コネクションステータスがアクティブになったときに、圧縮関数は、デフォルトのステータス情報により初期化される。圧縮においては、ロスがあってはならず、その長さは 1024バイト以上増加してはならない。展開関数において TLSCompressed.fragment を展開したとき、その長さが 2^14バイトを超えた場合は、fatal レベルの decompression failure エラーで応答しなければならない。
struct {
ContentType type; /* TLSPlaintext.type に同じ */
ProtocolVersion version;/* TLSPlaintext.version に同じ */
uint16 length;
opaque fragment[TLSCompressed.length];} TLSCompressed;
- length
- 次の TLSCompressed.fragment の長さ(単位バイト)。長さは 2^14 + 1024 を超えない。
- fragment
- TLSPlaintext.fragment を圧縮したもの。
注: CompressionMethod.null 演算が、唯一の演算である。他に代わるものはない。
実装上の注意:
展開を行う関数は、メッセージ処理により内部バッファのオーバーフローを起こすことがないことを保証する責任がある。
暗号化と MAC 関数は、TLSCompressed 構造体を、TLSCiphertext 構造体へ変換する。復号関数は、逆の処理を行う。また、レコードの MAC はシーケンス番号を含んでいるため、紛失、超過、繰り返しメッセージを検出することができる。
struct {
ContentType type;
ProtocolVersion version;
uint16 length;
select (CipherSpec.cipher_type) {case stream: GenericStreamCipher;
case block: GenericBlockCipher;} fragment;
} TLSCiphertext;
- type
- typeフィールドは、TLSCompressed.type と同じ。
- version
- versionフィールドは、TLSCompressed.version と同じ。
- length
- 次のTLSCiphertext.fragmentの長さ(単位バイト)。長さは 2^14 + 2048 を超えない。
- fragment
- TLSCompressed.fragmentを、MAC 付きで暗号化したもの。
6.2.3.1. Null または標準ストリーム暗号 English
ストリーム暗号(BulkCipherAlgorithm.null を含む。付録 A.6 を参照 。)は、TLSCompressed.fragment 構造体から TLSCiphertext.fragment 構造体への変換、またはその逆変換を行う。
stream-ciphered struct {
opaque content[TLSCompressed.length];
opaque MAC[CipherSpec.hash_size];} GenericStreamCipher;
MACは次のように生成される。
HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type +
TLSCompressed.version + TLSCompressed.length +
TLSCompressed.fragment));ここで、"+"は連鎖を意味する。
- seq_num
- このレコードのシーケンス番号。
- hash
- SecurityParameters.mac_algorithm で指定されるハッシュアルゴリズム。
MAC は、暗号化の前に計算されることに注意。ストリーム暗号では、MAC を含む全体のブロックを暗号化する。(RC4 などの)同期ベクトルを使用しないストリーム暗号では、ある1つのレコードの最後におけるストリーム暗号ステータスをそのまま、後続のパケット処理に使用する。もし CipherSuite が TLS_NULL_WITH_NULL_NULL であるならば、暗号化処理は、NULL変換として行われる (すなわち、データは暗号化されず、MAC が使用されないので MAC サイズはゼロである)。TLSCiphertext.length は 、TLSCompressed.length と CipherSpec.hash_size の和である。
(RC2 や DES などの)ブロック暗号の処理では、暗号化関数と MAC 関数により、 TLSCompressed.fragment構造体は TLSCiphertext.fragment 構造体へ、 TLSCiphertext.fragment 構造体は TLSCompressed.fragment 構造体へ変換される。
block-ciphered struct {
opaque content[TLSCompressed.length];
opaque MAC[CipherSpec.hash_size];
uint8 padding[GenericBlockCipher.padding_length];
uint8 padding_length;} GenericBlockCipher;
MAC は 6.2.3.1 節で記述されるように生成される。
- padding
- padding は、平文の長さがブロック暗号のブロック長の整数倍になるようにするために、データに追加されるものである。 TLSCiphertext.length がブロック長の整数倍になる限り、パディング長は 255 バイトまでのどんな長さでもよい。必要とするよりも長くパディングすることは、交換したメッセージ長の分析に基づくプロトコル攻撃を無力化するため、望ましいことかもしれない。パディングデータベクトルにおけるそれぞれの uint8 は、パディング長の値で埋められなければならない。
- padding_length
- パディング長は、GenericBlockCipher 構造体の全サイズが、その暗号におけるブロック長の整数倍になるようにすべきである。有効な値の範囲は、0 から 255 までである。padding_lengthはパディングフィールドの長さを指定するもので、padding_lengthフィールド自体の長さを含まない。
暗号化されたデータの長さ (TLSCiphertext.length) は、TLSCompressed.length、 CipherSpec.hash_size、およびpadding_lengthの合計よりさらに1バイト大きい値である。
例: ブロック長が 8バイト、コンテンツ長(TLSCompressed.length)が 61バイト、 MACの長さが 20バイトであるならば、パディングをする前の長さは 82バイトである。したがって、パディング長を 8 で割ったときの余りは、全ブロック長が 8バイト(ブロック長)の整数倍になるように、6でなければならない。パディング長は、6、14、22 から、254 までの値が可能である。 もしパディング長が、必要最小限である 6 であったとき、それぞれには6の値が含まれる。したがって、ブロック暗号化前のGenericBlockCipher における最後の 8 オクテットは、xx 06 06 06 06 06 06 06 となる。ただし、xx は MAC の最後のオクテットである。
注: CBC (Cipher Block Chaining) モードにおけるブロック暗号では、最初のレコードのための初期化ベクトル(IV)は、セキュリティパラメータが設定されるときに、別の鍵やシークレットによって生成される。以降のレコードの IV は、直前のレコードの最後の暗号ブロックである。
レコードプロトコルでは、ハンドシェイクプロトコルによって提供されるセキュリティパラメータから鍵、IV、および MAC シークレットを生成するためのアルゴリズムが必要である。
マスターシークレットは、安全なバイト列にハッシュされる。これはカレントのコネクションステータスにおいて必要とされる MAC シークレット、鍵、輸出のできない暗号における IV に割り当てられる(付録 A.6 を参照)。CipherSpecでは、クライアント書き出し MAC シークレット、サーバー書き出し MAC シークレット、クライアント書き出し鍵、サーバー書き出し鍵、クライアント書き出しIV、そしてサーバー書き出し IV を必要とする。これらはこの順序で、マスターシークレットから生成される。使用しない値は空である。
鍵と MAC シークレットを生成するときには、マスターシークレットをエントロピーソースとして使用する。そして乱数を使用して、法令上輸出可能とするため、暗号化されないソルトと IV が作成される。
鍵とする素材を生成するには、次の計算を行う。
key_block = PRF(SecurityParameters.master_secret,
"key expansion",
SecurityParameters.server_random +
SecurityParameters.client_random);
これを十分な出力が得られるまで行う。次に、key_block は以下の通りに仕切られる。
client_write_MAC_secret[SecurityParameters.hash_size]
server_write_MAC_secret[SecurityParameters.hash_size]
client_write_key[SecurityParameters.key_material_length]
server_write_key[SecurityParameters.key_material_length]
client_write_IV[SecurityParameters.IV_size]
server_write_IV[SecurityParameters.IV_size]client_write_IV と server_write_IV は、法令上、輸出のできないブロック暗号のためにのみ生成される。輸出可能なブロック暗号においては、初期化ベクトルは、以下に示すように後で生成される。余分な key_block マテリアルは捨てられる。
実装上の注意:
このドキュメントにおいて定義されている暗号スペックの中で、最も長いマテリアルを必要とするものは、3DES_EDE_CBC_SHA である。これは 2 x 24バイトの鍵、2 x 20 バイトの MAC シークレット、2 x 8 バイトのIV、合計 104 バイトの鍵とする素材を必要とする。輸出可能な暗号化アルゴリズム(CipherSpec.is_exportable が true) では、最終的な書き出し鍵を得るために、以下のような追加処理が必要となる。
final_client_write_key =
PRF(SecurityParameters.client_write_key,
"client write key",
SecurityParameters.client_random +
SecurityParameters.server_random);
final_server_write_key =
PRF(SecurityParameters.server_write_key,
"server write key",
SecurityParameters.client_random +
SecurityParameters.server_random);輸出可能な暗号化アルゴリズムでは、その IV は Hello メ ッセージにおける乱数から作成される。
iv_block = PRF("", "IV block", SecurityParameters.client_random +
SecurityParameters.server_random);この iv_block は、上記の key_block のときと同じように、2 つの初期化ベクトルに分割される。
client_write_IV[SecurityParameters.IV_size]
server_write_IV[SecurityParameters.IV_size]PRF は、この場合、シークレットなしで使用されることに注意。これは、シークレットは 0 バイトの長さを持ち、PRF のハッシュに何も寄与しないことを意味する。
TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 では、2 つの暗号化鍵に対しそれぞれ 5 バイトの乱数と、各 MAC 鍵に対しそれぞれ 16 バイト、合計 42 バイトの鍵とする素材を必要とする。PRF の出力は、key_blockに 格納される。key_block はさらに分割され、輸出可能な暗号化アルゴリズムであるために、書き出し鍵はソルト処理される。
key_block = PRF(master_secret,
"key expansion",
server_random +
client_random)[0..41]
client_write_MAC_secret = key_block[0..15]
server_write_MAC_secret = key_block[16..31]
client_write_key = key_block[32..36]
server_write_key = key_block[37..41]
final_client_write_key = PRF(client_write_key,
"client write key",
client_random +
server_random)[0..15]
final_server_write_key = PRF(server_write_key,
"server write key",
client_random +
server_random)[0..15]
iv_block = PRF("", "IV block", client_random +
server_random)[0..15]
client_write_IV = iv_block[0..7]
server_write_IV = iv_block[8..15]