1/* eslint-disable node-core/require-common-first, node-core/required-modules */ 2/* eslint-disable node-core/crypto-check */ 3 4'use strict'; 5const crypto = require('crypto'); 6const net = require('net'); 7 8exports.ccs = Buffer.from('140303000101', 'hex'); 9 10class TestTLSSocket extends net.Socket { 11 constructor(server_cert) { 12 super(); 13 this.server_cert = server_cert; 14 this.version = Buffer.from('0303', 'hex'); 15 this.handshake_list = []; 16 // AES128-GCM-SHA256 17 this.ciphers = Buffer.from('000002009c0', 'hex'); 18 this.pre_master_secret = 19 Buffer.concat([this.version, crypto.randomBytes(46)]); 20 this.master_secret = null; 21 this.write_seq = 0; 22 this.client_random = crypto.randomBytes(32); 23 24 this.on('handshake', (msg) => { 25 this.handshake_list.push(msg); 26 }); 27 28 this.on('server_random', (server_random) => { 29 this.master_secret = PRF12('sha256', this.pre_master_secret, 30 'master secret', 31 Buffer.concat([this.client_random, 32 server_random]), 33 48); 34 const key_block = PRF12('sha256', this.master_secret, 35 'key expansion', 36 Buffer.concat([server_random, 37 this.client_random]), 38 40); 39 this.client_writeKey = key_block.slice(0, 16); 40 this.client_writeIV = key_block.slice(32, 36); 41 }); 42 } 43 44 createClientHello() { 45 const compressions = Buffer.from('0100', 'hex'); // null 46 const msg = addHandshakeHeader(0x01, Buffer.concat([ 47 this.version, this.client_random, this.ciphers, compressions 48 ])); 49 this.emit('handshake', msg); 50 return addRecordHeader(0x16, msg); 51 } 52 53 createClientKeyExchange() { 54 const encrypted_pre_master_secret = crypto.publicEncrypt({ 55 key: this.server_cert, 56 padding: crypto.constants.RSA_PKCS1_PADDING 57 }, this.pre_master_secret); 58 const length = Buffer.alloc(2); 59 length.writeUIntBE(encrypted_pre_master_secret.length, 0, 2); 60 const msg = addHandshakeHeader(0x10, Buffer.concat([ 61 length, encrypted_pre_master_secret])); 62 this.emit('handshake', msg); 63 return addRecordHeader(0x16, msg); 64 } 65 66 createFinished() { 67 const shasum = crypto.createHash('sha256'); 68 shasum.update(Buffer.concat(this.handshake_list)); 69 const message_hash = shasum.digest(); 70 const r = PRF12('sha256', this.master_secret, 71 'client finished', message_hash, 12); 72 const msg = addHandshakeHeader(0x14, r); 73 this.emit('handshake', msg); 74 return addRecordHeader(0x16, msg); 75 } 76 77 createIllegalHandshake() { 78 const illegal_handshake = Buffer.alloc(5); 79 return addRecordHeader(0x16, illegal_handshake); 80 } 81 82 parseTLSFrame(buf) { 83 let offset = 0; 84 const record = buf.slice(offset, 5); 85 const type = record[0]; 86 const length = record.slice(3, 5).readUInt16BE(0); 87 offset += 5; 88 let remaining = buf.slice(offset, offset + length); 89 if (type === 0x16) { 90 do { 91 remaining = this.parseTLSHandshake(remaining); 92 } while (remaining.length > 0); 93 } 94 offset += length; 95 return buf.slice(offset); 96 } 97 98 parseTLSHandshake(buf) { 99 let offset = 0; 100 const handshake_type = buf[offset]; 101 if (handshake_type === 0x02) { 102 const server_random = buf.slice(6, 6 + 32); 103 this.emit('server_random', server_random); 104 } 105 offset += 1; 106 const length = buf.readUIntBE(offset, 3); 107 offset += 3; 108 const handshake = buf.slice(0, offset + length); 109 this.emit('handshake', handshake); 110 offset += length; 111 const remaining = buf.slice(offset); 112 return remaining; 113 } 114 115 encrypt(plain) { 116 const type = plain.slice(0, 1); 117 const version = plain.slice(1, 3); 118 const nonce = crypto.randomBytes(8); 119 const iv = Buffer.concat([this.client_writeIV.slice(0, 4), nonce]); 120 const bob = crypto.createCipheriv('aes-128-gcm', this.client_writeKey, iv); 121 const write_seq = Buffer.alloc(8); 122 write_seq.writeUInt32BE(this.write_seq++, 4); 123 const aad = Buffer.concat([write_seq, plain.slice(0, 5)]); 124 bob.setAAD(aad); 125 const encrypted1 = bob.update(plain.slice(5)); 126 const encrypted = Buffer.concat([encrypted1, bob.final()]); 127 const tag = bob.getAuthTag(); 128 const length = Buffer.alloc(2); 129 length.writeUInt16BE(nonce.length + encrypted.length + tag.length, 0); 130 return Buffer.concat([type, version, length, nonce, encrypted, tag]); 131 } 132} 133 134function addRecordHeader(type, frame) { 135 const record_layer = Buffer.from('0003030000', 'hex'); 136 record_layer[0] = type; 137 record_layer.writeUInt16BE(frame.length, 3); 138 return Buffer.concat([record_layer, frame]); 139} 140 141function addHandshakeHeader(type, msg) { 142 const handshake_header = Buffer.alloc(4); 143 handshake_header[0] = type; 144 handshake_header.writeUIntBE(msg.length, 1, 3); 145 return Buffer.concat([handshake_header, msg]); 146} 147 148function PRF12(algo, secret, label, seed, size) { 149 const newSeed = Buffer.concat([Buffer.from(label, 'utf8'), seed]); 150 return P_hash(algo, secret, newSeed, size); 151} 152 153function P_hash(algo, secret, seed, size) { 154 const result = Buffer.alloc(size); 155 let hmac = crypto.createHmac(algo, secret); 156 hmac.update(seed); 157 let a = hmac.digest(); 158 let j = 0; 159 while (j < size) { 160 hmac = crypto.createHmac(algo, secret); 161 hmac.update(a); 162 hmac.update(seed); 163 const b = hmac.digest(); 164 let todo = b.length; 165 if (j + todo > size) { 166 todo = size - j; 167 } 168 b.copy(result, j, 0, todo); 169 j += todo; 170 hmac = crypto.createHmac(algo, secret); 171 hmac.update(a); 172 a = hmac.digest(); 173 } 174 return result; 175} 176 177exports.TestTLSSocket = TestTLSSocket; 178