Upload
shigekiohtsu
View
17.116
Download
9
Embed Size (px)
Citation preview
SSL/TLSの基礎と最新動向セキュリティキャンプ 2015
2015年8月12日
IIJ 大津繁樹
更新版資料の置場 http://goo.gl/cX1M17
Github Repo: https://goo.gl/vRLzrj
自己紹介
•大津繁樹
•株式会社インターネットイニシアティブ• プロダクト本部アプリケーション開発部サービス開発2課
• NodeJS Technical Committee メンバー• (主にTLS/CRYPTO/OpenSSLバインディングを担当)
• IETF httpbis WG で HTTP/2相互接続試験等仕様策定に参画。
•ブログ: http://d.hatena.ne.jp/jovi0608/
はじめに• TLS(Transport Layer Security)の仕組みについて学んでいただきます。
•後述するようTLSは、様々なセキュリティの要素技術から成り立っており、その一つ一つが難解で深い技術要素です。
•残念ながら4時間もの時間があってもその全てを完全に理解することは容易ではありません。
本講義の目的
• TLSの本質は、要素技術をどう組み合わせてどういう手順でセキュアな通信を確立するかというところにあります。そのため、本講義の目的は、TLS技術の基礎
「TLSハンドシェイクの仕組み」
を理解していただくことです
•聞いているだけの理解より、実際に手を動かしての体験する方を重視して進めます。
•課題内容は空白にしています。講義当日にお知らせします。
本講義の内容
• TLSの概要
• TLSを理解する準備
• TLSの話• まずは TLS_RSA_WITH_AES_128_GCM_SHA256 の時から
• Perfect Forward Secrecy• TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256を使う
•最新情報 TLS1.3とはどういうものか?概要説明
まずやってみる
モジュールのインストール
$npm install seccamp2015-data-reader seccamp2015-tls
Simple TLS Client (simple_tls_client.js)の取得
$git clone https://github.com/shigeki/SecCamp2015-TLS
$cd SecCamp2015-TLS
$node simple_tls_client.js
キーボードを打ってみよう
(注:エラー処理やサニタイジングが十分でないのであくまでテスト用で利用すること)
TLSの概要
インターネットの脅威 盗聴
パスワードやクレジットカード番号を盗み見
インターネットの脅威 改ざん
通信途中でデータを書き換え
インターネットの脅威 なりすまし
ユーザになりすまして通信を行う
インターネットの脅威 否認
そんな通信してませんと否認する
インターネットの脅威から守るセキュリティ対策
盗聴
改ざん 成りすまし
否認
暗号化
完全性チェック
認証
署名
各レイヤーにおけるセキュリティ通信
WPA
IPsec
TLS,DTLS,SSH
S/MIME, PGP
無線LAN
IP
TCP, UDP
データ
TLSの目的
• TLSプロトコルの最重要なゴールは、通信する2つのアプリケーションの間でプライバシーとデータの完全性を提供することです。
RFC5246: The Transport Layer Security (TLS) Protocol Version 1.2
1. Introduction
The primary goal of the TLS protocol is to provide privacy and data
integrity between two communicating applications.
アプリ アプリ
完全性
プライバシー
TLSの簡単な歴史
• SSL 1.0未発表
• 1994年 SSL 2.0
• 1995年 SSL 3.0
• 1996年 IETF TLS WGスタート
• 1999年 TLS 1.0
• 2006年 TLS 1.1
• 2008年 TLS 1.2
• 2013年 TLS 1.3検討スタート
SSLは、旧ネットスケープ社の私的プロトコル
TLSと名前を変えて標準化
SSL3.0と基本設計は大きく変えず、内部バージョンは TLS1.0 =SSL 3.1
現在の利用推奨
様々な拡張仕様の追加
TLSを理解する準備
TLSの要素技術
X509証明書
PKI
対称暗号
暗号モード
公開鍵暗号
デジタル署名
メッセージ認証
乱数生成
TLS鍵交換
一方向ハッシュ
TLSプロトコルは、これらの要素技術を組み合わせてアプリ間のセキュア通信を確立する手順を決める
TLS要素技術の依存性
X509証明書
PKI
対称暗号
暗号モード
公開鍵暗号
デジタル署名 メッセージ認証
乱数生成
鍵交換 一方向ハッシュ
本来はこの一つ一つをきちんと理解してもらうのが必要
TLS要素技術の依存性
X509証明書
PKI
対称暗号
暗号モード
公開鍵暗号
デジタル署名 メッセージ認証
乱数生成
鍵交換 一方向ハッシュ
GCM
AES
ECDHE RSA
SHA256
HMAC
本日の講義で扱う技術
説明は概要だけですが、演習で実際の動作を手を動かして体験してもらいます。
要素技術とTLS CipherSuites
TLS_RSA_WITH_AES_128_GCM_SHA256 = {0x00,0x9C}
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256= {0xC0,0x2F};
対称暗号
暗号モード
デジタル署名
メッセージ認証(ハッシュ)
鍵交換TLS _ _ _WITH_ _ 鍵長 _ _
鍵交換・デジタル署名にRSA
対称暗号に128bit鍵長のAES
暗号モードにGCM
ハッシュにSHA256
番号として 0x00,0x9Cを割り当て
鍵交換にECDHE
デジタル署名にRSA
対称暗号に128bit鍵長のAES
暗号モードにGCM
ハッシュにSHA256
番号として 0xC0,0x2Fを割り当て
乱数生成
利用用途:
•暗号鍵(対称暗号:秘密鍵、公開鍵暗号:鍵ペア)の生成
•暗号モードの初期ベクトルやNonce(*)の生成
• MAC(メッセージ認証)用鍵
(* Nonce(number used once)一度だけ使い捨て用に使われる数字
求められる機能無作為性:偏りがなく等しい数である。予測不可能性:次の乱数が予想できない。制限負可能性:同じ乱数列を再現できない。
乱数生成
•実際の利用は、疑似乱数生成。seedが必要。
• OpenSSLでは、seedはLinuxの/dev/urandomを利用。WindowsではOSのAPIのCryptGenRandomだけでなく画面スクリーンのビットマップハッシュも利用。
脆弱性:
• CVE-2008-0166: DebianやUbuntuのOpenSSLに予測可能な乱数を生成してしまう脆弱性
• SSH公開鍵にブルートフォース攻撃
対称暗号
暗号文平文
共通鍵 共通鍵
平文
ストリーム暗号:データを逐次暗号化(RC4, Chacha20)
ブロック暗号:データをブロック毎に暗号化(DES, AES)
幾つかの暗号では既に危殆化:DES:2005年 NIST FPS46-3規格の廃止(2030年までは許容)
RC4: RFC7455: Prohibiting RC4 Cipher Suites
暗号化 復号化
対称暗号 AES
• 1997年よりプロジェクト開始、2000年選定、2001年仕様発行
•ブロックサイズ 128bit
•鍵長: 128bits, 192bits, 256bits の3種類
• Intel/AMDのCPUでハードウェア処理のサポート AES-NI
暗号モード
•ブロック暗号は同じデータを同じ鍵で暗号化すると毎回同一の暗号文になる。
•ブロック長より長いデータを暗号化する場合に暗号モードを利用して繰り返しを避ける。
• CBC:「(平文 XOR ベクトル) を暗号化」を続ける
• CTR:「カウンターを暗号化 XOR 平文」を続ける
実際にTLSで利用するには改ざん検知のためのMAC(メッセージ認証)との組み合わせる(AEAD)。
これまでの主流
これからの主流に(GCM後述)
AEAD(認証付き暗号)
暗号化しないけど改ざん防止が必要なデータ(ヘッダ等)
暗号化する平文
AEAD暗号化
暗号文 認証タグ
共通鍵
初期ベクトル
AEAD(認証付き暗号)
平文
AEAD復号化
改ざんチェック
暗号化しないけど改ざん防止が必要なデータ(ヘッダ等)
暗号文 認証タグ
共通鍵
初期ベクトル
GCM
• GCM (Galois Counter Mode: ガロアカウンターモード)
• CTRとGHASHを組み合わせたAEAD
•ハードウェア処理で高速化が可能
• AESと組み合わせて AES-GCMとして利用
一方向ハッシュ
データ 一方向ハッシュ関数
ハッシュ値
ハッシュ値を比較することでデータの改ざんをチェックすることができる。
一方向ハッシュ
• md5
• SHA-1
• SHA-2(SHA-256など6種)
• SHA-3(SHA3-256など6種)
2018年ぐらいには現実的なコストで衝突データを探せる見込み(*2)
既に現実的な攻撃手法が存在 (*1)
(*2) Cryptanalysis of SHA-1
https://www.schneier.com/blog/archives/2005/02/cryptanalysis_o.html
(*1) how to Break MD5 and Other Hash Functions
http://merlot.usc.edu/csac-f06/papers/Wang05a.pdf
8/5にNISTより正式公開
メッセージ認証(HMAC)
•事前に共通鍵を共有
•共通鍵とデータを組み合わせたハッシュ値を作成
•データの完全性とハッシュ作成者を認証する
データ 一方向ハッシュ関数
ハッシュ値
共通鍵
公開鍵暗号
512bit RSAの危険性 FREAK https://freakattack.com/
•解を求めるのが困難な数学的問題を利用して暗号を生成。
•公開鍵と秘密鍵のペアを生成。公開鍵はさらして大丈夫。
•公開鍵で暗号化し秘密鍵で復号化。
• RSA
• ECC(楕円曲線暗号)
公開鍵 秘密鍵
暗号化 復号化
鍵交換
• 2者間で安全に鍵を共有する仕組み
•互いに公開鍵を交換しあい、共有鍵を生成する。
•通信経路上で共有鍵のやり取りがない
• DH (Diffie-Hellman)
• ECDH(楕円曲線DH)
512bit DH Logjam https://weakdh.org/
デジタル署名
•データの完全性のチェックが可能となる。
•データの送信元の認証が可能となる。
•公開鍵の信頼性の範囲で否認防止が可能となる。
• RSA
• DSA,ECDSA
公開鍵秘密鍵 データ+デジタル署名
データハッシュ値を暗号化しデジタル署名を生成
デジタル署名を復号化。データハッシュ値と比較し検証する
X509証明書(Certificate)
•公開鍵と所有者・発行者やその他属性情報をデジタル署名したデータ
•元々は電子ディレクトリサービス仕様(X500)の一部
•認証局(CA)が所有者の実体を確認して証明書を発行する(PKI)
X509証明書(Certificate)
演習:
sudo npm install -g seccamp2015-crypto-workshopper
•乱数生成
• AES-GCM
•公開鍵暗号
•一方向ハッシュ
•鍵交換
•メッセージ認証
•デジタル署名
• X509証明書 (時間がなさそうなので取りやめました)
TLSの話
まずは TLS_RSA_WITH_AES_128_GCM_SHA256 の時から
注:複雑さを避けるためクライアント認証機能は説明しません
TLSデータ表現の仕方 (構造体,メンバー)
struct {
uint8 major;
uint8 minor;
} ProtocolVersion;
0 1
majaor minor
ProtocolVersion version = { 3, 3 }; /* TLS v1.2*/
0 1
0x03 0x03
8bit符号なし整数(2バイト)
構造体と同じ
構造体名
TLSデータ表現の仕方(配列・エンディアン)
struct {
uint32 gmt_unix_time;
opaque random_bytes[28];
} Random;
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
gmt_unix_time random_bytes
32bit符号なし整数(4バイト)
1970/1/1 0:00(UTC)からの経過秒数
28バイト長の任意のバイト列[]の中はバイト数を表す。
データを書き込むときは、最上位のバイトデータをインデックスの小さい方から大きい方への順番で書き込む
(big-endian/network byte order)
Mon Jul 20 2015 01:55:29 GMT+0900 (JST) = 1437324929 sec = 0x55abd681
0 1 2 3
55 ab d6 81
TLSデータ表現の仕方 (可変ベクター)
opaque SessionID<0..32>;
<>は可変ベクター型を表す。中の数字は、<最少サイズ..最大サイズ>
先頭にサイズを必ず入れ、その後にデータが続く。先頭のサイズの領域は最大サイズが格納できる量。左の場合は最大32バイトだから1バイト(255まで表現可能)分のサイズ領域があればよい。
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
0x20 SessionID(32バイト)
0 1
0x01 SessionID(1バイト)
0
0x00SessionIDのサイズが0の場合
SessionIDのサイズが1の場合
SessionIDのサイズが32の場合
TLSデータ表現の仕方 (enum)
uint8 CipherSuite[2];
enum { null(0), (255) } CompressionMethod;
8bit符号なし整数が2バイト
0 1
0x00 0x40
CipherSuite TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = { 0x00,0x40 };
CompressionMethodの値は最大255まで(1バイト分)NULLは0に割り当てられています。
TLSデータ表現の仕方 (条件式)
struct {
ProtocolVersion client_version;
Random random;
SessionID session_id;
CipherSuite cipher_suites<2..2^16-2>;
CompressionMethod compression_methods<1..2^8-1>;
select (extensions_present) {
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ClientHello;
条件式extensionが存在しなければ、空してれば、0~2^16-1までの可変ベクターのデータが入る
TLSデータ表現の仕方(読んでみよう)
struct {
ExtensionType extension_type;
opaque extension_data<0..2^16-1>;
} Extension;
enum {
signature_algorithms(13), (65535)
} ExtensionType;
TLSデータ表現の仕方(読んでみよう)enum {
none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
sha512(6), (255)
} HashAlgorithm;
enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) }
SignatureAlgorithm;
struct {
HashAlgorithm hash;
SignatureAlgorithm signature;
} SignatureAndHashAlgorithm;
SignatureAndHashAlgorithm
supported_signature_algorithms<2..2^16-2>;
TLSデータ表現の仕方(読んでみよう)struct {
uint8 major;
uint8 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;
TLS1.2の構造I
Pヘッダ
T
C
Pヘッダ
TLS Record Layer(5バイト)
タイプ(4種類)
(1byte)
バージョン
(2byte)
長さ
(2byte)Handshake (タイプ:0x16)
msgタイプ(10種類)
長さ(3バイト長)
ハンドシェイクデータ
Alert (タイプ:0x15)
レベル 理由
ChangeCipherSpec (タイプ:0x14)
タイプ
Application Data (タイプ:0x17)
暗号化されたデータ
msgタイプ ハンドシェイクデータの種類
0x00 HelloRequest
0x01 ClientHello
0x02 ServerHello
0x0b Certificate
0x0c ServerKeyExchange
0x0d CertificateRequest
0x0e ServerHelloDone
0x0f CertificateVerify
0x10 ClientKeyExchange
0x14 Finished
TLS Record Layerデータに続いて、次の4種類のTLSデータのいずれかが続く。
TLS Handshakeは、この10種類に分かれる。
TLSハンドシェイクデータの構造struct {
HandshakeType msg_type;
uint24 length;
select (HandshakeType) {
case hello_request: HelloRequest;
case client_hello: ClientHello;
case server_hello: ServerHello;
case certificate: Certificate;
case server_key_exchange: ServerKeyExchange;
case certificate_request: CertificateRequest;
case server_hello_done: ServerHelloDone;
case certificate_verify: CertificateVerify;
case client_key_exchange: ClientKeyExchange;
case finished: Finished;
} body;
} Handshake;
enum {
hello_request(0), client_hello(1),
server_hello(2), certificate(11),
server_key_exchange (12), certificate_request(13),
server_hello_done(14), certificate_verify(15),
client_key_exchange(16), finished(20) (255)
} HandshakeType;
Handshake (タイプ:0x16)
msgタイプ(10種類)
長さ(3バイト長)
ハンドシェイクデータ
msgタイプの種類でそれぞれの構造を参照する
TLSハンドシェイク
まずは TLS_RSA_WITH_AES_128_GCM_SHA256 の時から
TLSハンドシェイク(full handshake)ClientHello
ServerHello
Certificate
ServerHelloDone
ClientKeyExchange
ChangeCipherSpec
Finished
ChangeCipherSpec
Finished
Application Data
Application Data
(赤文字はハンドシェイク)
ClientHelloとServerHelloのやり取りで双方が利用するTLSバージョンや暗号化方式などを合意する。
TLSハンドシェイク(resumption)
ClientHello
ServerHello
ChangeCipherSpec
Finished
ChangeCipherSpec
Finished
Application Data
Application Data
(赤文字はハンドシェイク)
SessionIDによるTLSセッションの再開。鍵交換や証明書送付をスキップ。
今回は演習の対象外です
TLSハンドシェイクの意味
ClientHello/ServerHello/ServerHelloDoneTLSのための情報交換
バージョン・乱数・暗号方式・拡張情報
Certificate公開鍵情報の送付エンドポイントの認証
ClientKeyExchange/ServerKeyExchange共有鍵交換
ChangeCipherSpec暗号開始の合図
Finishedハンドシェイクデータの改ざんチェック
ClientHello
struct {
ProtocolVersion client_version;
Random random;
SessionID session_id;
CipherSuite cipher_suites<2..2^16-2>;
CompressionMethod compression_methods<1..2^8-1>;
select (extensions_present) {
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ClientHello;
ClientHello項目 要素 サイズ 先頭の長さ情報
client_version uint8 major, uint8 minor 2 N/A
random uint32 gmt_unix_time, opaque random_bytes[28] 4 + 28 N/A
session_id opaque SessionID <0..32> 1バイト分
cipher_suites uint8 CipherSuite[2] <2..2^16-2> 2バイト分
compression_met
hods
null(0) <1..2^8-1> 1バイト分
extensions extension_type(65535), extension_data<0..2^16-1> <0..2^16-1> 2バイト分
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
type データ長
データ type データ長
データ type データ長
データ
Extension長
Extensionsデータ例
ClientHello
Record Layer Handshake (ClientHello)
type protocol
version
length
(2byte)
msg
type
length
(3byte)
client
version
random session
id
cipher
suite
compr
ession
Extensi
on
major minor major minor
0x16 0x03 0x01 ?? ?? 0x01 ?? ?? ?? 0x03 0x03 32 byte 可変 可変 可変 可変
Version0x03,0x00 = SSLv3
0x03,0x01= TLSv1.0
0x03,0x02=TLSv1.1
0x03,0x03=TLSv1.2
クライアントが利用できる最高のTLSバージョンを指定、サーバがどのバージョンを使うか選択する
Record Layer Handshake (ClientHello)
type protocol
version
length
(2byte)
msg
type
length
(3byte)
client
version
random session
id
cipher
suite
compression
major minor major minor
0x16 0x03 0x01 0x00
0x2D
0x01 0x00
0x00
0x29
0x03 0x03 32 byte
All 0x00
0x00 0x00,0x02
0x00,0x9c
0x01
0x00
CipherSuite指定(1種)
TLS1.2用
TLS_RSA_WITH_AES_128_GCM_SHA256:0x00,0x9c
41 byte長45 byte長
最小のClientHello
'160301002D' '0100290303'
crypto.randomBytes(32).toString('hex')
'000002009C0100'本当はちゃんとした乱数を入れる
+
+
+
演習:最少ClientHelloを作って送るhttps://gist.github.com/shigeki/d880ae0b4eef3cfd1acc
var net = require('net');
var crypto = require('crypto');
var recordheader = '160301002D';
var random = crypto.randomBytes(32).toString('hex');
var handshake = '010000290303' + random + '000002009C0100';
var clienthello = new Buffer(recordheader + handshake, 'hex');
var client = net.connect({host: 'tls.koulayer.com', port: 443}, function() {
client.write(clienthello);
});
client.on('data', function(c) {
// Receive ServerHello
console.log(c);
});
ServerHello
struct {
ProtocolVersion server_version;
Random random;
SessionID session_id;
CipherSuite cipher_suite;
CompressionMethod compression_method;
select (extensions_present) {
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ServerHello;
複数ではない
複数ではない
ServerHello項目 要素 サイズ 先頭の長さ情報
server_version uint8 major, uint8 minor 2 N/A
random uint32 gmt_unix_time, opaque random_bytes[28] 4 + 28 N/A
session_id opaque SessionID <0..32> 1
cipher_suite uint8 CipherSuite[2] 2 N/A
compression_met
hod
null(0) 1 N/A
extensions extension_type, extension_data<0..2^16-1> <0..2^16-1> 2バイト分
Record Layer(5bytes) Handshake (ServerHello)
type protocol
version
length
(2bytes)
msg
type
length
(3byte)
server
version
random
32bytes
session id cipher
suite
2bytes
compression
major minor major minor
0x16 0x03 0x03 ? + 4 0x01 ? 0x03 0x03 ? 長さ1byte 0x00,0x9c 長さ2bytes
DataReaderを使うhttps://github.com/shigeki/seccamp2015-data-reader
データを読み込むHelperオブジェクト
var DataReader = require('seccamp2015-data-reader').DataReader;
var reader = new DataReader(buffer);
API
• reader.readBytes(n):前回読み込んだ位置からnバイト分のデータをバッファで返す
• reader. readVector(floor, ceiling):可変ベクターのデータを読み込み、バッファで返す。(floor:最小値、ceiling:最大値)
• reader. peekBytes(from, n): from個目からto個目までのバッファをコピーして返す
• reader. bytesRemaining(): 残っているバイトサイズを返す
演習: Record Headerを見る
function parseRecordHeader(reader) {
var type = reader.readBytes(1).readUInt8(0);
var version = reader.readBytes(2);
var length = reader.readBytes(2).readUIntBE(0, 2);
return {type: type, version: version, length: length};
}Record Layer
type
(1byte)
protocol
version
length
(2bytes)
major
(1byte)
minor
(1byte)
演習 ServerHelloを見るhttps://gist.github.com/shigeki/67c35e12b0834fca3821
function parseServerHello(reader) {
var record_header = parseRecordHeader(reader);
var type = reader.readBytes(1).readUInt8(0);
var length = reader.readBytes(3).readUIntBE(0, 3);
var version = reader.readBytes(2);
var random = reader.readBytes(32);
var session_id = reader.readVector(0, 32);
var cipher = reader.readBytes(2);
var compression = reader.readBytes(1);
return {
record_header: record_header,
type: type,
length: length,
version: version,
random: random,
session_id: session_id,
cipher: cipher,
compression: compression
};
}
Handshake (ServerHello)
msg
type
length
(3byte)
server
version
random
32bytes
session id cipher
suite
2bytes
compression
major minor
Certificate
opaque ASN.1Cert<2^24-1>;
struct {
ASN.1Cert certificate_list<0..2^24-1>;
} Certificate;
項目 要素 サイズ
certificate_list ASN.1Cert<2^24-1> <0..2^24-1>
最大値のみ3バイト分
全証明書長 証明書#1長 証明書データ#1 証明書#2長 証明書データ#2
3バイト分複数の証明書データを送付
最初は必ずサーバ証明書 2つ目以降は中間証明書など
ここから先の演習は状態管理が必要
はじめに使った simple_tls_client.js を使います。
モジュールのインストール
$npm install seccamp2015-data-reader seccamp2015-tls
Simple TLS Client (simple_tls_client.js)の取得
$git clone https://github.com/shigeki/SecCamp2015-TLS
$cd SecCamp2015-TLS
$node simple_tls_client.js
キーボードを打ってみよう
(注:エラー処理やサニタイジングが十分でないのであくまでテスト用で利用すること)
演習 Certificateを見るhttps://github.com/shigeki/SecCamp2015-TLS/blob/master/index.js
function parseCertificate(reader, state) {
if (!checkRecordHeader(reader))
return null;
var record_header = parseRecordHeader(reader);
storeHandshakeData(reader, record_header.length, state);
var type = reader.readBytes(1).readUInt8(0);
var length = reader.readBytes(3).readUIntBE(0, 3);
var cert_reader = new DataReader(reader.readBytes(length));
var certlist = [];
while(cert_reader.bytesRemaining() > 0) {
var cert = cert_reader.readVector(0, 1 << 24 - 1);
certlist.push(cert);
}
return {
record_header: record_header,
type: type,
length: length,
certlist: certlist
};
}
証明書はリストに格納
3バイト分
ServerKeyExchange
RSA鍵交換では不要。今の時点じゃスキップします。
ECDHEのところで再度説明します。
ServerHelloDone
struct { } ServerHelloDone;
handshake type handshake長
0x0e 0x00 0x00 0x00
ServerHelloの終了の合図ハンドシェイクヘッダのみ
ClientKeyExchange
struct {
select (KeyExchangeAlgorithm) {
case rsa:
EncryptedPreMasterSecret;
case dhe_dss:
case dhe_rsa:
case dh_dss:
case dh_rsa:
case dh_anon:
ClientDiffieHellmanPublic;
} exchange_keys;
} ClientKeyExchange;
鍵交換のアルゴリズムによって異なる
RSAによる鍵交換では暗号化された
PreMasterSecretを送る
DH/DHEによる鍵交換ではクライアントの公開鍵を送る。
ClientKeyExchange (RSA鍵交換の場合)struct {
ProtocolVersion client_version;
opaque random[46];
} PreMasterSecret;
struct {
public-key-encrypted PreMasterSecret pre_master_secret;
} EncryptedPreMasterSecret;
ClientHelloで送付したバージョン
通常は証明書に記載されているRSA
公開鍵でpre_master_secretを暗号化
エラー処理が脆弱性がなる場合もある
<0..2^16-1>の可変ベクター
ClientKeyExchange (RSA鍵交換の場合)
Record Layer(5bytes) Handshake(ClientKeyExchange)
type protocol
version
length
(2bytes)
msg
type
length
(3byte)Encrypted PreMasterSecret
major minor
0x16 0x03 0x03 ? + 4 0x10 ? 長さ2バイト ?
PreMasterSecret
client version random 46bytes
major minor
0x03 0x01
TLSの鍵生成の流れpre master secret
(任意のバイト数:鍵交換による)サーバ・クライアント間の鍵交換方式で生成し、秘密的に共有する
master secret(48 bytes)
PRF(pre_master_secret, "master secret", client_random+server_random)
keyblock(任意のバイト数:利用暗号方式による)
PRF(master_secret, "key expansion", server_random+client_random)
client_write_MAC server_write_MAC client_write_key server_write_key client_write_IV server_write_IV
PreMasterSecret/MasterSecret
• TLSで利用するIV(初期ベクトル)、共有鍵、MAC鍵のデータ元
• MasterSecretは48バイト長。PreMasterSecretの長さは鍵交換方式に依存する。
• MasterSecretは、PreMasterSecret、ClientRandom、ServerRandom、固定ラベルから生成する。
• Clinet/ServerRandomは全て丸見え。PreMasterSecretは、必ず死守して守らないといけない。これが漏えいするとTLSの安全性は全ておじゃん。
演習:PreMasterSecretの計算TLS1.2のRSA鍵交換時
function createPreMasterSecretRSAKeyExchange(state) {
var pre_master_secret = (ここを作る)
return pre_master_secret;
}
演習答え:PreMasterSecretの計算TLS1.2のRSA鍵交換時(48バイト)
function createPreMasterSecretRSAKeyExchange(state) {
var version = new Buffer('0303', 'hex');
var pre_master_secret = Buffer.concat([version, crypto.randomBytes(46)]);
return pre_master_secret;
}ClientHelloで送ったVersion 2バイト分
残り46バイトは乱数で発生させる
演習: ClientKeyExchangeを作るvar clientkeyexchange_json = {
pre_master_secret: pre_master_secret,
pubkey: require('fs').readFileSync(__dirname + '/pubkey.pem')
};
var clientkeyexchange = createClientKeyExchange(clientkeyexchange_json, state);
function createClientKeyExchange(json, state) {
state.handshake.clientkeyexchange_json = json;
var public_key = json.pubkey;
var pre_master_secret = json.pre_master_secret;
var encrypted = crypto.publicEncrypt({
key: public_key,
padding: require('constants').RSA_PKCS1_PADDING
}, pre_master_secret);
var encrypted_pre_master_secret = writeVector(encrypted, 0, 1 << 16 - 1);
var handshake = createHandshake(handshake_type.clientkeyexchange, encrypted_pre_master_secret);
return createRecord(type.handshake, handshake);
};
公開鍵情報は、本当は Certificate データから抽出する。パーサーが必要なので別ファイルから読み込む
公開鍵で pre_master_secretを暗号化
MasterSecretの計算その1: P_hash
PreMasterSecretのサイズは、鍵交換方式で異なる。
MasterSecretは48バイト必要。
P_hash: データ拡張関数。あるsecretを必要なサイズまで伸長させる。
P_hash(secret, seed) = HMAC_hash(secret, A(1)+seed) + HMAC_hash(secret, A(2)+seed) + …. (必要なサイズまで伸長)
A(0) = seed;
A(i) = HMAC_hash(secret, A(i-1))
+はデータの結合を表す。 HMAC_hashのアルゴリズムは、TLS1.2では SHA256を使う(それ以前はMD5/SHA-1の組み合わせ)。
演習: TLS1.2の P_hashを作るvar crypto = require('crypto');
var P_hash = require('seccamp2015-tls').P_hash;
var algo = 'sha256';
var secret = 'mysecret';
var seed = (new Buffer(32)).fill(0);
var size = 48;
function MyP_hash(algo, secret, seed, size) {
var ret = new Buffer(size);
(ここを作る)
return ret;
}
var answer = P_hash(algo, secret, seed, size);
var my_answer = MyP_hash(algo, secret, seed, size);
console.log(answer);
console.log(my_answer);
console.log(answer.equals(my_answer));
function MyP_hash(algo, secret, seed, size) {
var ret = new Buffer(size);
var hmac = crypto.createHmac(algo, secret);
hmac.update(seed);
var a = hmac.digest(); // A(1)
var end = 0;
while(size > end) {
hmac = crypto.createHmac(algo, secret);
hmac.update(Buffer.concat([a, seed]));
var b = hmac.digest();
var len = (size - end >= b.length) ? b.length: size - end;
b.copy(ret, end, 0, len);
end += len;
hmac = crypto.createHmac(algo, secret);
hmac.update(a);
a = hmac.digest(); // A(i+1)
}
return ret;
}
演習:答え
HMAC_hash(secret, A(i)+seed)
サイズ分まで結合する
次のA(i)を計算
A(0)はseed
A(1)を計算
MasterSecretの計算その2PRF(PseudoRandom Function)
PRF(secret, label, seed) = P_hash(secret, label + seed)
master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)
[0..47];
= P_hash(pre_master_secret, "master secret" +
ClientHello.random +ServerHello.random)[0..47]
固定ラベル
48バイトまで計算
重要:PRFはTLSで他にも様々な要素の計算に用いられます。
演習: TLS1.2 master_secretを計算するvar crypto = require('crypto');
var PRF12 = require('seccamp2015-tls').PRF12;
var pre_master_secret = crypto.randomBytes(48);
var client_random = crypto.randomBytes(32);
var server_random = crypto.randomBytes(32);
function MyPRF12(secret, label, seed, size) {
return MyP_hash('sha256', secret, Buffer.concat([label, seed]), size);
}
var label = new Buffer("master secret");
var master_secret = PRF12(pre_master_secret, label, Buffer.concat([client_random, server_random]), 48);
var Mymaster_secret = MyPRF12(pre_master_secret, label, Buffer.concat([client_random, server_random]), 48);
console.log(master_secret);
console.log(Mymaster_secret);
console.log(master_secret.equals(Mymaster_secret));
ラベルは
master secret
labelとseedを結合して新しい seed を生成
master_secretは48バイト
KeyExpansion
• TLSで必要な鍵は、 MAC用、共有鍵用、初期ベクトル(IV)用の3種類。Client/Serverそれぞれが必要なので6つ必要。
•それぞれで必要なサイズは暗号方式で異なる。
• TLSで必要な鍵は48バイトのMasterSecretでは、サイズが足りない。
• MasterSecretからPRFで必要なサイズのkey_blockに伸長させる。
client_write_MAC server_write_MAC client_write_key server_write_key client_write_IV server_write_IV
master_secret(48バイト)
key_block = PRF(master_secret, "key expansion", server_random + client_random);
注意!:master_secret
の計算時と順番が逆
KeyExpansion(AES-128-GCM)
• client_write_MAC, server_write_MAC: 0バイトx2 (AEADはMAC不要)
• client_write_key, server_write_key: 16バイトx2 (128bits鍵長)
• client_write_IV, server_write_IV: 4バイトx2 (12バイト中の頭、後述)
合計: 40バイト
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
client_write_key(16bytes) server_write_key(16bytes) client_write
_IV(4bytes)
server_write
_IV(4bytes)
key_block = PRF12(algo, master_secret, "key expansion", ServerHello.random+ClientHello.random, 40);
演習:KeyBlockの計算function KDF(pre_master_secret, client_random, server_random) {
var master_secret = PRF12(pre_master_secret, "master secret", Buffer.concat([client_random, server_random]), 48);
// 40 bytes key_block for AES-128-GCM
var key_block_reader = new DataReader(
PRF12(master_secret, "key expansion", Buffer.concat([server_random, client_random]), 40));
return {
master_secret: master_secret,
client_write_MAC_key: null,
server_write_MAC_key: null,
client_write_key: key_block_reader.readBytes(16),
server_write_key: key_block_reader.readBytes(16),
client_write_IV: key_block_reader.readBytes(4),
server_write_IV: key_block_reader.readBytes(4)
};
}
ラベルは
key expansion
結合の順番が反対
AES-128-GCMは40バイト分必要
GCMはMACはいらない
AES-128の秘密鍵は16バイト
初期ベクトルの最初の4バイト分は両者で秘密共有、残り8バイトはフレームに付加して送る
ChangeCipherSpec
struct {
enum { change_cipher_spec(1), (255) } type;
} ChangeCipherSpec;
送信元が暗号開始を宣言。これを送信した後は暗号通信を行う。
Record Layer ChangeCipherSpec
ContentType Version length
(2byte)major minor
0x14 0x03 0x03 0x00 0x01 0x01
Finished
struct {
opaque verify_data[verify_data_length];
} Finished;
verify_data = PRF(master_secret, finished_label, Hash(handshake_messages))[0..11];
finished_label: クライアントは、"client finished"、サーバは"server finished"
12バイト固定
これまでのハンドシェイクデータ(ただし自分は除く)のハッシュを計算TLS1.2では SHA256を使う
Finishedを受信すると、これまで送受信したハンドシェイクデータから計算した値と比較。ハンドシェイクデータが改ざんされてないことを確認する。
Finishedを作るvar clientfinished = {
master_secret: state.keyblock.master_secret,
handshake_message_list: state.handshake_message_list
};
var clientfinished = createClientFinished(clientfinished, state);
function createClientFinished(json, state) {
state.handshake.clientfinished = json;
// create session hash
var shasum = crypto.createHash('sha256');
shasum.update(Buffer.concat(json.handshake_message_list));
var message_hash = shasum.digest();
var r = PRF12(json.master_secret, "client finished", message_hash, 12);
var handshake = createHandshake(handshake_type.finished, r);
return createRecord(type.handshake, handshake);
}
これまで交換したハンドシェイクデータのリスト
これまで交換したハンドシェイクデータのハッシュ値を求める
PRFで12バイト分に切り詰める
フレーム作成後暗号化
受信したFinished中の値と比較して改ざんチェック
データの暗号化struct {
ContentType type;
ProtocolVersion version;
uint16 length;
select (SecurityParameters.cipher_type) {
case stream: GenericStreamCipher;
case block: GenericBlockCipher;
case aead: GenericAEADCipher;
} fragment;
} TLSCiphertext;
レコードフィールド
暗号化した後の長さ
ストリーム暗号、ブロック暗号、AEAD
で構造が変わる。
Record Layer 暗号化されたデータ
ContentType Version length
(2byte)major minor
0x03 0x03 …..
データの暗号化(AEAD)struct {
opaque nonce_explicit[record_iv_length];
aead-ciphered struct {
opaque content[TLSCompressed.length];
};
} GenericAEADCipher;
AEADEncrypted = AEAD-Encrypt(write_key, nonce, plaintext, additional_data)
struct {
opaque salt[4];
opaque nonce_explicit[8];
} GCMNonce;
additional_data = seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length;
key blockから生成した4bytes
毎回生成する乱数(8bytes 丸見え)
uint160から始まる
認証データ
暗号化する前の長さ
TLSレコード層の情報
AEAD(AES-128-GCM)で暗号化されたデータRecord Header explicit nonce
(8 byte)
暗号化されたデータ tag
(16 bytes)Cont
entTy
pe
Version length
(2byte)major minor
0x03 0x03
AAD
writeSeq + Record Header暗号化する平文
AES-128-GCM
暗号化されたデータ tag
write_key
初期ベクトル
writeIV(4bytes) + explicit
nonce(8bytes)
explicit nonce、暗号データ、tag を合わせた長さに再計算
アプリケーションデータの暗号化function EncryptAEAD(frame, state) {
var key = state.keyblock.client_write_key;
var iv = Buffer.concat([state.keyblock.client_write_IV.slice(0,4), state.nonce_explicit]);
var record_header = frame.slice(0, 5);
var aad = Buffer.concat([state.write_seq, record_header]);
var ret = Encrypt(frame.slice(5), key, iv, aad);
var encrypted = ret.encrypted;
var tag = ret.tag;
// re-calcuate length with adding nonce_explit and tag length
var length = state.nonce_explicit.length + encrypted.length + tag.length;
record_header.writeUIntBE(length, 3, 2);
// increment write sequence number
incSeq(state.write_seq);
return Buffer.concat([record_header, state.nonce_explicit, encrypted, tag]);
}
record長の再計算
sequence noを増加
初期ベクトルはwrite_IV(4) とexplicit nonceを合わせたもの
秘密鍵漏洩の脅威
• TLS_RSA_WITH_AES_128_GCM_SHA256は、鍵交換・署名にRSAを利用している。
•通信経路に pre_master_secretを暗号化して相手に送信。
•公開鍵・秘密鍵共に長期に固定化されている。
•暗号強度が低い又は秘密鍵が漏えいしてしまうとどうなるか?
演習: TLSを破る
時間があれば行います。
サーバの秘密鍵をお渡しします。
秘密鍵とハンドシェイクデータから暗号化されたアプリ通信を解読する。
演習: TLSを破る
• https://github.com/shigeki/SecCamp2015-TLS/tree/master/break_tls
•秘密鍵のパスワードは口頭でお伝えします。
戻し方
$ openssl rsa -in server_encrypted.key -out server.key
Enter pass phrase for server_encrypted.key:
writing RSA key
秘密鍵と handshake.jsonのデータから暗号化されたApplication Dataを復号してください。
演習回答:
Record Layer Handshake (ClientHello)
type protocol
version
length
(2byte)
msg
type
length
(3byte)
client
version
random session
id
cipher
suite
compr
ession
Extensi
on
major minor major minor
0x16 0x03 0x01 ?? ?? 0x01 ?? ?? ?? 0x03 0x03 32 byte 可変 可変 可変 可変
5+4+2=11bytes 先から32byte
Record Layer(5bytes) Handshake(ClientKeyExchange)
type protocol
version
length
(2bytes)
msg
type
length
(3byte)Encrypted PreMasterSecret
major minor
0x16 0x03 0x03 ? + 4 0x10 ? 長さ2バイト ?
Client/Server Randomの取得
5+4+2=11bytes 先から最後まで
暗号化されたpre master secretの取得
秘密鍵で復号化
演習回答:
Record Header explicit nonce
(8 byte)
暗号化されたデータ tag
(16 bytes)Cont
entTy
pe
Version length
(2byte)major minor
0x03 0x03
sequence noと合わせて AAD
ただし長さは再計算が必要writeIVと合わせてIV
復号化でセットするタグ値
ここを復号化して平文をゲットする
演習回答:solve.js
var DataReader = require('seccamp2015-data-reader').DataReader;
var SecCampTLS = require('seccamp2015-tls');
var fs = require('fs'), crypto = require('crypto');
var handshake = require(__dirname + '/handshake.json');
var clienthello = new Buffer(handshake.ClientHello, 'hex');
var serverhello = new Buffer(handshake.ServerHello, 'hex');
var clientkeyexchange = new Buffer(handshake.ClientKeyExchange, 'hex');
var encryptedApplicationData = new Buffer(handshake.EncryptedApplicationData, 'hex');
// obtain handshake parameters
var client_random = clienthello.slice(11, 11+32);
var server_random = serverhello.slice(11, 11+32);
var encrypted_pre_master_secret = clientkeyexchange.slice(11);
// obtain private key
var private_key = fs.readFileSync(__dirname + '/server.key');
JSONデータからハンドシェイクデータを抽出
ハンドシェイクパラメータを抽出
秘密鍵の入手
演習回答:
// decrypt pre master secret
var pre_master_secret = crypto.privateDecrypt(
{key: private_key,
padding: require('constants').RSA_PKCS1_PADDING
}, encrypted_pre_master_secret);
// objtain keyblock
var keyblock = SecCampTLS.KDF(pre_master_secret, client_random, server_random);
// Calculate Sequence Number
var read_seq = (new Buffer(8)).fill(0);
read_seq[7] = 0x01;
秘密鍵を使って ClientKeyExchangeで暗号化されたpre master secret の復号化
pre master secretとclient/server random
を使ってkey block を生成
sequence number を生成
演習回答:
var reader = new DataReader(encryptedApplicationData);
// Obtain AEAD parameters
var record_header = reader.readBytes(5);
var length = record_header.readUIntBE(3, 2);
var frame = reader.readBytes(length);
var nonce_explicit = frame.slice(0, 8);
var encrypted = frame.slice(8, frame.length - 16);
var tag = frame.slice(-16);
// Re-Caluclate record header
record_header.writeUIntBE(encrypted.length, 3, 2);
var iv = Buffer.concat([keyblock.client_write_IV, nonce_explicit]);
var aad = Buffer.concat([read_seq, record_header]);
暗号化されたアプリケーションデータからAEADパラメータを抽出する
record headerの長さ再計算
演習回答:
// Decrypt Application Data
var decipher = crypto.createDecipheriv('aes-128-gcm', keyblock.client_write_key, iv);
decipher.setAuthTag(tag);
decipher.setAAD(aad);
var clear = decipher.update(encrypted);
decipher.final();
console.log(clear.toString());
これまで得られたパラメータからデータの復号化
平文の取得
Perfect Forward Secrecy
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256を使う
Perfect Forward Secrecy(PFS)
•前方秘匿性
• セッション毎に一時的な鍵を使う。
• ハンドシェイクを含む全暗号データを取得されているような状況でも、将来的な秘密鍵漏洩などのリスクに対応する。
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
Ephemeral:一時的な鍵交換手法
ECDHEのハンドシェイク
ClientHello
+ elliptic_curves(10)
+ ec_point_formats(11)ServerHello
+ ec_point_formats(11)
Certificate
ServerKeyExchange
ServerHelloDone
ClientKeyExchange
ChangeCipherSpec
FinishedChangeCipherSpec
FinishedApplication Data
(赤文字が追加変更されるところ)
ClientHello拡張を追加。
ServerHello拡張を追加。
楕円曲線名とServerの公開鍵を署名付きで送付
Clientの公開鍵を送付
ECPointの書式を合意
使える楕円曲線名とECPoint書式を通知
ECDHE ClientHello拡張
struct { NamedCurve elliptic_curve_list<1..2^16-1> } EllipticCurveList;
enum {
sect163k1 (1), 省略, secp256r1 (23), 省略, (0xFFFF)
} NamedCurve; クライアントがサポートしている楕円曲線のリストをサーバ側に通知。サーバはリストの中から適切な楕円曲線を選び ServerKeyExchange内で選択した楕円曲線を通知する
0 1 2 3 4 5 6 7
elliptic_curves(10) リスト長 データ長 secp256r1 (23)
0x00 0x0a 0x00 0x04 0x00 0x02 0x00 0x17
ECDHE Client/Server Hello拡張
enum { uncompressed (0),省略, reserved (248..255)
} ECPointFormat;
struct {
ECPointFormat ec_point_format_list<1..2^8-1>
} ECPointFormatList;
楕円暗号の公開鍵の書式
0 1 2 3 4 5
ec_point_formats(11) リスト長 データ長 uncompressed(0)
0x00 0x0b 0x00 0x02 0x01 0x00
ECDHE ServerKeyExchange
select (KeyExchangeAlgorithm) {
case ec_diffie_hellman:
ServerECDHParams params;
Signature signed_params;
} ServerKeyExchange;
struct {
ECParameters curve_params;
ECPoint public;
} ServerECDHParams;
struct {
SignatureAndHashAlgorithm algorithm;
opaque signature<0..2^16-1>;
} DigitallySigned;
ServerECDHParams Signature
ECParameters ECPoint algorithm
signaturecurve_type named_curve 長さ public key(Hello拡張指定の書式)
RSA-SHA256
(0x04,0x01)named_curve (3) secp256r1 (23)
signature = sign(algorithm, ClientHello.random + ServerHello.random + ServerECDHParams);
RSA秘密鍵で
ServerECDHParmsとRandomを署名
ECDHE ClientKeyExchangestruct {
select (KeyExchangeAlgorithm) {
case ec_diffie_hellman: ClientECDiffieHellmanPublic;
} exchange_keys;
} ClientKeyExchange;
struct {
select (PublicValueEncoding) {
case implicit: struct { };
case explicit: ECPoint ecdh_Yc;
} ecdh_public;
} ClientECDiffieHellmanPublic;
ECDHEは explicit
ClientECDHParams
ECPoint
長さ public key(Hello拡張指定の書式)
ClientKeyExchangeは署名の必要はない
演習: ECDHE公開鍵の守られ方の違い
• ServerKeyExchange: 公開鍵を署名
• ClientKeyExchange: やりたい放題
どうしてでしょう?
TLS最新情報TLS1.3(*)とはどういうものか?
(* 注: 2015-7-23時点のものです。最終仕様で変更になる可能性があります。)
TLS1.3(*)の背景
• 2008年 TLS1.2仕様化から7年
•古い暗号やプロトコルの危殆化に伴う機能の抜本的な見直し
•常時TLS化を目指し、将来的に安心で強固なセキュリティの必要性
•モバイル環境の普及やWebアプリの高度化に伴う高パフォーマンス、低レイテンシー化要望の高まり
TLS1.3の目的
1. ハンドシェイクデータをできるだけ暗号化して隠匿する
2. ハンドシェイクレイテンシーの削減• 再接続の場合は、 0-RTT
• フルハンドシェイクは、 1-RTT
3. ハンドシェイクで交換する項目の見直し
4. レコード層暗号化の見直し(RC4廃止、CBCの不採用)
TLS1.3の構造(*)
I
Pヘッダ
T
C
Pヘッダ
TLS Record Layer(5バイト)
タイプ(4種類)
(1byte)
バージョン0x0301に固定
(2byte)
長さ
(2byte)
Handshake (タイプ:0x16)
msgタイプ(10種類)
長さ ハンドシェイクデータ
Alert (タイプ:0x15)
レベル 理由
Application Data (タイプ:0x17)
暗号化されたデータ
msgタイプ
ハンドシェイクデータの種類
0x00 reserved(旧HelloRequest廃止)
0x01 ClientHello
0x02 ServerHello
0x04 NewSessionTicket
0x06 HelloRetryRequest
0x07 ServerKeyShare
0x0b Certificate
0x0c reserved(旧ServerKeyExchange廃止)
0x0d CertificateRequest
0x0e ServerConfigration
0x0f CertificateVerify
0x10 reserved(旧ClientKeyExchange廃止)
0x14 Finished
TLS Handshakeは、この12種類に増加。後方互換のため3つは予約。
Early Handshake (タイプ:0x19)
msgタイプ 長さ ハンドシェイクデータ
ChangeCipherSpecを廃止
(* 注: 2015-7-20時点のものです。最終仕様で変更になる可能性があります。)
Extens
ion ID
Extension
0x00d signature_algorithms
TBD early_data
TBD supported_groups
TBD known_configration
TBD pre_shared_key
TBD client_key_shares
TLS1.3 ハンドシェイク (full handshake)
ServerHello
ServerKeyShare
EncryptedExtensions
ServerConfigration
Certificate
Finished
Finished
Application Data
Application Data
(赤文字は暗号化されているデータ)
ClientHello
+ ClientKeyShare
1-RTT
Application Data
ここで即暗号化
ServerHello
ServerKeyShare
EncryptedExtensions
ServerConfigration
Certificate
Finished
Finished
Application Data
Application Data
(赤文字は暗号化されているデータ)
ClientHello
+ ClientKeyShare
2-RTT
Application Data
TLS1.3 ハンドシェイク (鍵交換ができなかった時)
ClientHello
+ ClientKeyShare
HelloRetryRequest
ここで即暗号化
TLS1.3 ハンドシェイク (再接続)
ServerHello
+ KnownConfigration
+ EarlyDataIndication
ServerKeyShare
Finished
Finished
Application Data
(赤文字は暗号化されているデータ)
ClientHello
+ ClientKeyShare
+ KnownConfigration
+ EarlyDataIndication
ApplicationData
0-RTT
Application Data
以前のサーバ公開鍵と組み合わせてフライイングでアプリデータ暗号化して送る
TLS1.3 0-RTTの問題点
0-RTT
ApplicationData
POST / HTTP/1.1
xxxx
0-RTT
ApplicationData
POST / HTTP/1.1
xxxx
クラスタリング厳密な同期は不可能
Reply攻撃
Reply攻撃に弱いため、最初のデータは冪等性のあるリクエストのみに制限しなければならない
更新
更新
TLS1.3 鍵生成(Server view)
Ephemeral Secret Static Secret
xES xSS
Master Secret
Early Data
Traffic Keys
Application
Traffic Keys
Handshake
Traffic Keys
Exporter
Secret
Resumption
Secret
Finished
Secret
Server SecretKey
in KnownConfigration
Client PubKey
in ClientKeyShare
ClientPubKey
in ClientKeyShareServer SecretKey
DH or ECDH鍵交換
HKDF
DH or ECDH鍵交換
HKDF
HKDF seedHKDF key
HKDF(label + session_hash)
ハンドシェイク暗号化用 アプリデータ暗号化用 Finished用
以前に送付した古いの。最初なら鍵生成したもの。
TSL1.3変更点まとめ(*)• Compression, Renegotiationの廃止
• ChangeCipherSpec廃止
• ClientKeyExchange廃止してClientHelloの拡張フィールドに
• HelloRequest/ServerHello廃止
• Record層のバージョンを 0x03,0x03(TLS1.0)に固定
• ServerConfigrationをクライアントに渡し、0-RTT接続を実現
• ServerKeyShare後即暗号化、証明書データも隠匿
•乱数から時間の gmtフィールド廃止
(* 注: 2015-7-23時点のものです。最終仕様で変更になる可能性があります。)
TSL1.3変更点まとめ(*)
•鍵交換は名前付きDH,ECDHで ephemeral利用のみ
•鍵生成は、HKDF利用に変更。Ephemeral/Staticの2種類のsecretからsession_hashを付けて各用途別に生成。
•暗号モードで利用できるのはAEADのみ(GCM, CCM, Poly1305)• AEADの初期ベクタ廃止
• Record層のLengthをAEADで認証外に
•他にも色々・・・
(* 注: 2015-7-23時点のものです。最終仕様で変更になる可能性があります。)