• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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