Fedor Indutny
Node.js core (TLS, Crypto)
Understanding crypto in C could be...
-
Fast scripting language
-
Bare protocol
-
Simple OpenSSL bindings
TLS.js
https://github.com/indutny/tls.js
TLS.js
npm install tls.js
MiTM
-
WiFi ssid + key
-
+
-
RSA private key
-
= fun!
Toggle your wifi connection
https://nodeconf.indutny.com
-
submit some messages
Fork MiTM.js
https://github.com/indutny/mitm.js
How does TLS work, anyway?
RFC 5246
-
...is scary
-
lots of pages
-
such... wow
Without minor nits, version differences, compatibility...
Some useful info first...
Collection of algorithms
-
Key exchange (DH, DHE, ECDH, DCHE)
-
Auth (RSA, DSA, ECDSA)
-
Cipher (AES, DES, ChaCha, IDEA)
-
Hash (MD5, SHA1, SHA256)
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
Preference criteria
-
1. PFS (ECDHE, DHE)
-
2. AES256, AES-GCM
-
3. Stronger hashes (SHA256, SHA384, not MD5)
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/
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);
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:
-
Algorithms
-
But don't have keys, IVs
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);
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
Bud
https://github.com/indutny/bud
JSON config
{
"workers": 8,
"frontend": {
"key": "key.pem",
"cert": "cert.pem",
"port": 443
},
"backend": [{
"host": 80
}]
}
-
(a)?sync SNI routing
-
async OCSP stapling
-
Backend balancing
-
DTrace hooks
-
DSO logger
JSON proxy
BUD {"family":"tcp4","peer":{"host":...,"port":...},...}
GET / HTTP/1.1
$ npm install -g bud-tls
$ bud -h
$ bud --default-config > conf.json
$ vim conf.json
$ bud --config conf.json
Looks like I have more time!
node.js v0.12 TLS changes
-
TLS moved to C++
-
ECDHE/DHE cipher support
-
Async OCSP stapling