TLS

whoami

Fedor Indutny


Node.js core (TLS, Crypto)

SSL / TLS

Understanding crypto in C could be...

But what if...

TLS.js

https://github.com/indutny/tls.js

TLS.js

npm install tls.js

demo time

MiTM

Toggle your wifi connection

Fork MiTM.js

https://github.com/indutny/mitm.js

How does TLS work, anyway?

RFC 5246

Without minor nits, version differences, compatibility...

Some useful info first...

Cipher Suite

Collection of algorithms

algorithms.join('-');
algorithms.join('-');
'AES256-SHA'; // RSA is omitted here
algorithms.join('-');
'AES256-SHA'; // RSA is omitted here
'ECDHE-RSA-AES256-SHA384';
algorithms.join('-');
'AES256-SHA'; // RSA is omitted here
'ECDHE-RSA-AES256-SHA384';
'NULL-MD5'; // Don't use it!

List of supported ciphers

$ openssl ciphers -v
DHE-RSA-AES256-SHA
DHE-DSS-AES256-SHA
AES256-SHA
...

Server configuration

openssl ciphers -v \
ECDHE-RSA-AES256-SHA384:AES256-SHA:AES128-SHA

Order of preference

Preference criteria

Support older clients

My recommendation

http://git.io/Vqkt9g

tls.createServer({
  key: ...,
  cert: ...,
  honorCipherOrder: true,
  ciphers:
    'ECDHE-RSA-AES256-GCM-SHA384:' +
    'ECDHE-RSA-AES128-GCM-SHA256:...'
}, ...)

Check your server here

ssllabs.com/ssltest/

Now back to TLS.js

Framer

var state = tls.state.createDummy({});
var framer = tls.framer.create(state);
framer.hello('client', {
  cipherSuites: [],
  random: new Buffer(32)
});
framer.read();  // <Buffer 16 03 03 ...
// or framer.pipe(socket)

Parser

var parser = tls.parser.create(state);
framer.pipe(parser);
framer.hello('client', {
  cipherSuites: [],
  random: new Buffer(32)
});
parser.read();  // output on next slide

Parser

{ version: 0x0303, // TSL1.2
  maxVersion: 0x0303, // TLS1.2
  type: 'handshake',
  handshakeType: 'client_hello',
  random: ...,
  session: false,
  cipherSuites: [],
  cipherSuite: null,
  extensions: {} }
  

State machine

if (frame.type === 'alert')
  handled = this.handleAlert(frame);
else if (frame.type === 'handshake')
  handled = this.handleHandshake(frame);
else if (frame.type === 'change_cipher_spec')
  handled = this.handleChangeCipher(frame);
else if (frame.type === 'application_data')
  handled = this.handleAppData(frame);
  

Handshake example

ClientHello

this.framer.hello('client', {
  maxVersion: 0x0303,  // TLS1.2
  cipherSuites: [
    // Pick some from:
    // `tls.constants.cipherSuite`
    'TLS_RSA_WITH_AES_128_CBC_SHA'
  ],
  extensions: {
    server_name: 'deathstar.empire.com',
  }
});

ServerHello

var cipher = this.selectCipherSuite(
    frame.cipherSuites);
if (!cipher)
  return false;
  
this.framer.hello('server', {
  cipherSuite: cipher
});

Certificate (usually)

this.framer.certificate([
  new Buffer(...),
  new Buffer(...)
]);

and ServerHelloDone

this.framer.helloDone();

Now both sides know:

ClientKeyExchange

var preMaster = new Buffer(2 + 46);
preMaster.writeUInt16BE(this.version, 0, true);
this.crypto.random(46).copy(preMaster, 2);

this.pending.preMaster = preMaster;

ClientKeyExchange (pt 2)

var out = new Buffer(2 + pub.size());
this.crypto.encryptPublic(out.slice(2),
                          preMaster,
                          pub);
                          
out.writeUInt16BE(out.length - 2, 0, true);
this.framer.keyExchange('client', out);

Meanwhile on server

var preMaster = new Buffer(48);
preMaster = this.crypto.decryptPrivate(
    preMaster,
    keyEx,
    this.key.key);
    

Both have shared

PreMaster secret

Generate keys, through

PRF expansion

PRF expansion

// Pseudo-code
Math.setRandomSeed(preMaster)
while (still_needed)
  out.push((Math.random() * 256) | 0);
  

Both sides ready to encrypt

ChangeCipherSpec/Finished

// All consequent data will be encrypted
this.framer.changeCipherSpec();
// Verify the handshake integrity
this.switchToPending('write', function() {
  self.framer.finished(verifyData);
});

Established!

client.emit('secure');
server.emit('secure');

client.write('GET / HTTP/1.1\r\n\r\n');
server.write('HTTP/1.1 200 OK\r\n\r\n' +
             'hello world');
             

Fork TLS.js

https://github.com/indutny/tls.js

Fork OpenSSL

https://github.com/openssl/openssl

FIN

Side notes

Bud

https://github.com/indutny/bud

TLS babel-fish

Features:

Runs on libuv

JSON config

{
  "workers": 8,
  "frontend": {
    "key": "key.pem",
    "cert": "cert.pem",
    "port": 443
  },
  "backend": [{
    "host": 80
  }]
}

JSON proxy

BUD {"family":"tcp4","peer":{"host":...,"port":...},...}
GET / HTTP/1.1

Bud is fun!

$ npm install -g bud-tls
$ bud -h
$ bud --default-config > conf.json
$ vim conf.json
$ bud --config conf.json

FIN

FIN.

FIN..

FIN...

Looks like I have more time!

node.js v0.12 TLS changes

crypto changes:

FIN