Comparing version 0.1.7 to 0.2.0
@@ -6,2 +6,3 @@ /** | ||
* @overview | ||
* @author Matthew Caruana Galizia <m@m.cg> | ||
* @author Andris Reinman <andris.reinman@gmail.com> | ||
@@ -18,23 +19,67 @@ * @author Nathan Rajlich <nathan@tootallnate.net> | ||
/*jshint node:true*/ | ||
/*global exports:true*/ | ||
var net = require('net'); | ||
var tls = require('tls'); | ||
var crypto = require('crypto'); | ||
module.exports = exports = function(options, onSecure) { | ||
var socket, securePair; | ||
if (options instanceof net.Socket) { | ||
socket = options; | ||
options = { | ||
socket: socket | ||
}; | ||
} else if (options.socket) { | ||
socket = options.socket; | ||
} else { | ||
socket = options.socket = net.createConnection(options); | ||
} | ||
if (options.pair) { | ||
securePair = options.pair; | ||
} else { | ||
securePair = tls.createSecurePair(crypto.createCredentials(), false); | ||
options.pair = securePair; | ||
} | ||
if (socket.readable) { | ||
return startTls(options, onSecure); | ||
} | ||
socket.once('connect', function() { | ||
startTls(options, onSecure); | ||
}); | ||
return securePair; | ||
}; | ||
exports.startTls = function(socket, onSecure) { | ||
var credentials, securePair, clearText; | ||
return startTls({ | ||
socket: socket, | ||
pair: tls.createSecurePair(crypto.createCredentials(), false) | ||
}, onSecure); | ||
}; | ||
function startTls(options, onSecure) { | ||
var socket, host, securePair, clearText; | ||
socket = options.socket; | ||
host = options.host; | ||
securePair = options.pair; | ||
socket.ondata = null; | ||
socket.removeAllListeners('data'); | ||
credentials = crypto.createCredentials(); | ||
securePair = tls.createSecurePair(credentials, false); | ||
clearText = pipe(securePair, socket); | ||
securePair.on('secure', function() { | ||
var verifyError = securePair.ssl.verifyError(); | ||
securePair.once('secure', function() { | ||
var err; | ||
// A cleartext stream has the boolean property 'authorized' to determine if it was verified by the CA. If 'authorized' is false, a property 'authorizationError' is set on the stream. | ||
if (verifyError) { | ||
err = securePair.ssl.verifyError(); | ||
if (err) { | ||
clearText.authorized = false; | ||
clearText.authorizationError = verifyError; | ||
clearText.authorizationError = err; | ||
} else { | ||
@@ -44,6 +89,12 @@ clearText.authorized = true; | ||
// The callback parameter is optional | ||
if (onSecure) { | ||
onSecure(); | ||
// The callback parameter is optional. | ||
if (!onSecure) { | ||
return; | ||
} | ||
if (host && !tls.checkServerIdentity(host, clearText.getPeerCertificate())) { | ||
err = new Error('Server identity mismatch: invalid certificate for ' + host + '.'); | ||
} | ||
onSecure.call(securePair, err); | ||
}); | ||
@@ -54,7 +105,8 @@ | ||
return securePair; | ||
}; | ||
} | ||
function forwardEvents(events, emitterSource, emitterDestination) { | ||
var i, l, map = [], name, handler; | ||
var forwardEvent = function() { | ||
var i, l, event, handler, forwardEvent; | ||
forwardEvent = function() { | ||
this.emit.apply(this, arguments); | ||
@@ -64,16 +116,14 @@ }; | ||
for (i = 0, l = events.length; i < l; i++) { | ||
name = events[i]; | ||
handler = forwardEvent.bind(emitterDestination, name); | ||
map.push(name); | ||
emitterSource.on(name, handler); | ||
event = events[i]; | ||
handler = forwardEvent.bind(emitterDestination, event); | ||
emitterSource.on(event, handler); | ||
} | ||
return map; | ||
} | ||
function removeEvents(map, emitterSource) { | ||
for (var i = 0, l = map.length; i < l; i++){ | ||
emitterSource.removeAllListeners(map[i]); | ||
function removeEvents(events, emitterSource) { | ||
var i, l; | ||
for (i = 0, l = events.length; i < l; i++){ | ||
emitterSource.removeAllListeners(events[i]); | ||
} | ||
@@ -83,4 +133,5 @@ } | ||
function pipe(securePair, socket) { | ||
var clearText, onError, onClose, eventsMap; | ||
var clearText, onError, onClose, events; | ||
events = ['timeout', 'end', 'drain']; | ||
clearText = securePair.cleartext; | ||
@@ -97,7 +148,7 @@ | ||
socket.removeListener('close', onClose); | ||
removeEvents(eventsMap, socket); | ||
removeEvents(events, socket); | ||
}; | ||
// Forward event emissions from the socket to the cleartext stream | ||
eventsMap = forwardEvents(['timeout', 'end', 'drain'], socket, clearText); | ||
// Forward event emissions from the socket to the cleartext stream. | ||
forwardEvents(events, socket, clearText); | ||
socket.on('error', onError); | ||
@@ -104,0 +155,0 @@ socket.on('close', onClose); |
{ | ||
"name": "starttls", | ||
"description": "Upgrade a regular `net.Stream` connection to a secure `tls` connection.", | ||
"version": "0.1.7", | ||
"version": "0.2.0", | ||
"main": "lib/starttls.js", | ||
@@ -25,2 +25,8 @@ "homepage": "https://github.com/mattcg/starttls", | ||
], | ||
"scripts": { | ||
"test": "make test" | ||
}, | ||
"devDependencies": { | ||
"mocha": "1.x" | ||
}, | ||
"bugs": { | ||
@@ -27,0 +33,0 @@ "url": "https://github.com/mattcg/starttls/issues" |
# Start TLS # | ||
Upgrade a regular [`net.Stream`](http://nodejs.org/api/net.html#net_class_net_socket) connection to a secure [`tls`](http://nodejs.org/api/tls.html) connection. See [socks5-https-client](https://github.com/mattcg/socks5-https-client) for use-case. | ||
Upgrade a regular [`net.Stream`](http://nodejs.org/api/net.html#net_class_net_socket) connection to a secure [`tls`](http://nodejs.org/api/tls.html) connection. | ||
@@ -9,13 +9,33 @@ Based on a version by [Andris Reinman](https://github.com/andris9/rai/blob/master/lib/starttls.js), itself based on an older version by [Nathan Rajlich](https://gist.github.com/TooTallNate/848444). | ||
The only method, `startTls`, returns a [`SecurePair`](http://nodejs.org/api/tls.html#tls_class_securepair) and takes a [`Socket`](http://nodejs.org/api/net.html#net_class_net_socket) and an optional callback called on the [`secure`](http://nodejs.org/api/tls.html#tls_event_secure) event of the pair. | ||
This library has one method and accepts either an options hash or a prepared socket as the first argument. It returns a [`SecurePair`](http://nodejs.org/api/tls.html#tls_class_securepair). | ||
### starttls(options, [onSecure]), starttls(socket, [onSecure]) ### | ||
The following options are supported: | ||
- `socket` - if not provided, a socket will be created using [`net.createConnection`](http://nodejs.org/api/net.html#net_net_createconnection_options_connectionlistener) | ||
- `host` - used to perform automatic certificate identity checking, to guard against MITM attacks | ||
- `port` - only used to create a socket (along with the `host` option) if `socket` is not provided | ||
- `pair` - if you want to provide your own [`SecurePair`](http://nodejs.org/api/tls.html#tls_class_securepair) object | ||
The `onSecure` callback is optional and receives `null` or an error object as the first argument (see below for error cases). Within the callback context, `this` refers to the same [`SecurePair`](http://nodejs.org/api/tls.html#tls_class_securepair) object returned by `starttls`. | ||
```javascript | ||
var net = require('net'); | ||
var startTls = require('starttls').startTls; | ||
var port = 21; | ||
var socket = net.createConnection(port); | ||
var starttls = require('starttls'); | ||
var options = { | ||
port: 21, | ||
host: example.com | ||
}; | ||
socket.on('connect', function() { | ||
var securePair = startTls(socket, function() { | ||
securePair.cleartext.write('garbage'); | ||
net.createConnection(options, function() { | ||
options.socket = this; | ||
starttls(options, function(err) { | ||
if (err) { | ||
// Something bad happened! | ||
return; | ||
} | ||
this.cleartext.write('garbage'); | ||
}); | ||
@@ -25,2 +45,27 @@ }); | ||
You should always check for an error before writing to the stream to avoid man-in-the-middle attacks. Errors are produced in the following cases: | ||
- the certificate authority authorization check failed or was negative | ||
- the server identity check was negative | ||
If you only pass a socket object, server identity checking will not be performed automatically. In that case you should perform the check manually. | ||
```javascript | ||
starttls(socket, function(err) { | ||
if (!tls.checkServerIdentity(host, this.cleartext.getPeerCertificate())) { | ||
// Hostname mismatch! | ||
// Report error and end connection... | ||
} | ||
}); | ||
``` | ||
## Example ## | ||
See [socks5-https-client](https://github.com/mattcg/socks5-https-client) for use-case. | ||
## Tests ## | ||
Run `make test` or `npm test` to run tests. | ||
## License ## | ||
@@ -30,2 +75,2 @@ | ||
Modified and redistributed under an [MIT license](https://github.com/andris9/rai/blob/master/LICENSE). | ||
Modified and redistributed under an [MIT license](http://mattcg.mit-license.org/). |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
10183
6
218
74
1
4