nodejs/test/common/tls.js

178 lines
5.6 KiB
JavaScript

/* eslint-disable node-core/require-common-first, node-core/required-modules */
/* eslint-disable node-core/crypto-check */
'use strict';
const crypto = require('crypto');
const net = require('net');
exports.ccs = Buffer.from('140303000101', 'hex');
class TestTLSSocket extends net.Socket {
constructor(server_cert) {
super();
this.server_cert = server_cert;
this.version = Buffer.from('0303', 'hex');
this.handshake_list = [];
// AES128-GCM-SHA256
this.ciphers = Buffer.from('000002009c0', 'hex');
this.pre_master_secret =
Buffer.concat([this.version, crypto.randomBytes(46)]);
this.master_secret = null;
this.write_seq = 0;
this.client_random = crypto.randomBytes(32);
this.on('handshake', (msg) => {
this.handshake_list.push(msg);
});
this.on('server_random', (server_random) => {
this.master_secret = PRF12('sha256', this.pre_master_secret,
'master secret',
Buffer.concat([this.client_random,
server_random]),
48);
const key_block = PRF12('sha256', this.master_secret,
'key expansion',
Buffer.concat([server_random,
this.client_random]),
40);
this.client_writeKey = key_block.slice(0, 16);
this.client_writeIV = key_block.slice(32, 36);
});
}
createClientHello() {
const compressions = Buffer.from('0100', 'hex'); // null
const msg = addHandshakeHeader(0x01, Buffer.concat([
this.version, this.client_random, this.ciphers, compressions
]));
this.emit('handshake', msg);
return addRecordHeader(0x16, msg);
}
createClientKeyExchange() {
const encrypted_pre_master_secret = crypto.publicEncrypt({
key: this.server_cert,
padding: crypto.constants.RSA_PKCS1_PADDING
}, this.pre_master_secret);
const length = Buffer.alloc(2);
length.writeUIntBE(encrypted_pre_master_secret.length, 0, 2);
const msg = addHandshakeHeader(0x10, Buffer.concat([
length, encrypted_pre_master_secret]));
this.emit('handshake', msg);
return addRecordHeader(0x16, msg);
}
createFinished() {
const shasum = crypto.createHash('sha256');
shasum.update(Buffer.concat(this.handshake_list));
const message_hash = shasum.digest();
const r = PRF12('sha256', this.master_secret,
'client finished', message_hash, 12);
const msg = addHandshakeHeader(0x14, r);
this.emit('handshake', msg);
return addRecordHeader(0x16, msg);
}
createIllegalHandshake() {
const illegal_handshake = Buffer.alloc(5);
return addRecordHeader(0x16, illegal_handshake);
}
parseTLSFrame(buf) {
let offset = 0;
const record = buf.slice(offset, 5);
const type = record[0];
const length = record.slice(3, 5).readUInt16BE(0);
offset += 5;
let remaining = buf.slice(offset, offset + length);
if (type === 0x16) {
do {
remaining = this.parseTLSHandshake(remaining);
} while (remaining.length > 0);
}
offset += length;
return buf.slice(offset);
}
parseTLSHandshake(buf) {
let offset = 0;
const handshake_type = buf[offset];
if (handshake_type === 0x02) {
const server_random = buf.slice(6, 6 + 32);
this.emit('server_random', server_random);
}
offset += 1;
const length = buf.readUIntBE(offset, 3);
offset += 3;
const handshake = buf.slice(0, offset + length);
this.emit('handshake', handshake);
offset += length;
const remaining = buf.slice(offset);
return remaining;
}
encrypt(plain) {
const type = plain.slice(0, 1);
const version = plain.slice(1, 3);
const nonce = crypto.randomBytes(8);
const iv = Buffer.concat([this.client_writeIV.slice(0, 4), nonce]);
const bob = crypto.createCipheriv('aes-128-gcm', this.client_writeKey, iv);
const write_seq = Buffer.alloc(8);
write_seq.writeUInt32BE(this.write_seq++, 4);
const aad = Buffer.concat([write_seq, plain.slice(0, 5)]);
bob.setAAD(aad);
const encrypted1 = bob.update(plain.slice(5));
const encrypted = Buffer.concat([encrypted1, bob.final()]);
const tag = bob.getAuthTag();
const length = Buffer.alloc(2);
length.writeUInt16BE(nonce.length + encrypted.length + tag.length, 0);
return Buffer.concat([type, version, length, nonce, encrypted, tag]);
}
}
function addRecordHeader(type, frame) {
const record_layer = Buffer.from('0003030000', 'hex');
record_layer[0] = type;
record_layer.writeUInt16BE(frame.length, 3);
return Buffer.concat([record_layer, frame]);
}
function addHandshakeHeader(type, msg) {
const handshake_header = Buffer.alloc(4);
handshake_header[0] = type;
handshake_header.writeUIntBE(msg.length, 1, 3);
return Buffer.concat([handshake_header, msg]);
}
function PRF12(algo, secret, label, seed, size) {
const newSeed = Buffer.concat([Buffer.from(label, 'utf8'), seed]);
return P_hash(algo, secret, newSeed, size);
}
function P_hash(algo, secret, seed, size) {
const result = Buffer.alloc(size);
let hmac = crypto.createHmac(algo, secret);
hmac.update(seed);
let a = hmac.digest();
let j = 0;
while (j < size) {
hmac = crypto.createHmac(algo, secret);
hmac.update(a);
hmac.update(seed);
const b = hmac.digest();
let todo = b.length;
if (j + todo > size) {
todo = size - j;
}
b.copy(result, j, 0, todo);
j += todo;
hmac = crypto.createHmac(algo, secret);
hmac.update(a);
a = hmac.digest();
}
return result;
}
exports.TestTLSSocket = TestTLSSocket;