55. 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 可変 可変 可変 可変
Version
0x03,0x00 = SSLv3
0x03,0x01= TLSv1.0
0x03,0x02=TLSv1.1
0x03,0x03=TLSv1.2
クライアントが利用で
きる最高のTLSバー
ジョンを指定、サーバ
がどのバージョンを使
うか選択する
56. 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' 本当はちゃんとし
た乱数を入れる
+
+
+
57. 演習: 最少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);
});
58. 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;
複数ではない
複数ではない
59. 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
60. 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(): 残っているバイトサイズを返す
61. 演習: 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)
62. 演習 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
70. 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
77. 演習: 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));
78. 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)を計算
89. 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 を合わせた長さに再計算
90. アプリケーションデータの暗号化
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を合わせたもの
94. 演習回答:
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の取得
秘密鍵で復号化
95. 演習回答:
Record Header explicit nonce
(8 byte)
暗号化されたデータ tag
(16 bytes)Cont
entTy
pe
Version length
(2byte)
major minor
0x03 0x03
sequence noと合わせて AAD
ただし長さは再計算が必要
writeIVと合わせてIV
復号化でセットするタグ値
ここを復号化して平
文をゲットする
96. 演習回答: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データからハンド
シェイクデータを抽出
ハンドシェイクパラ
メータを抽出
秘密鍵の入手
97. 演習回答:
// 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 を生成
98. 演習回答:
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の長さ再計算
99. 演習回答:
// 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());
これまで得られたパラメー
タからデータの復号化
平文の取得