mirror of https://gitee.com/openkylin/nodejs.git
178 lines
5.6 KiB
JavaScript
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;
|