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