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