Need help with secure-peer?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

205 Stars 15 Forks Other 74 Commits 4 Opened issues


peer-to-peer encrypted streams using public key cryptography and signing

Services available


Need anything else?

Contributors list


Create encrypted peer-to-peer streams using public key cryptography and signing.

No certificates, no authorities. Each side of the connection has the same kind of keys so it doesn't matter which side initiates the connection.

build status


First generate some public/private keypairs with rsa-json:

$ rsa-json > a.json
$ rsa-json > b.json
var secure = require('secure-peer');
var peer = secure(require('./a.json'));
var through = require('through');

var net = require('net'); var server = net.createServer(function (rawStream) { var sec = peer(function (stream) { stream.pipe(through(function (buf) { this.emit('data', String(buf).toUpperCase()); })).pipe(stream); }); sec.pipe(rawStream).pipe(sec); }); server.listen(5000);

var secure = require('secure-peer');
var peer = secure(require('./b.json'));

var net = require('net'); var rawStream = net.connect(5000);

var sec = peer(function (stream) { stream.pipe(process.stdout); stream.end('beep boop\n'); }); sec.pipe(rawStream).pipe(sec);

sec.on('identify', function (id) { // you can asynchronously verify that the key matches the known value here id.accept(); });

For extra security, you should keep a file around with known hosts to verify that the public key you receive on the first connection doesn't change later on like how


Maintaining a known hosts file is outside the scope of this module.


implements a very simple protocol that gives gives confidential content, although the identity of the peers (pubkeys) are leaked to a passive evesdropper.

The protocol begins by sending a handshake code

    var outgoing = {
        token : crypto.randomBytes(64).toString('base64'),
        key : {
            type : 'rsa',
            public : keys.public,
        dh : {
            group :,
            public : dh.getPublicKey('base64')
        ciphers : ciphers

The header contains a token (used to prevent replay attacks), your public key, a new diffie-helman public key, and the list of supported ciphers.

This is then signed with the corresponding private key, and sent as a line of json. code All binary values are base64 encoded.

Upon receiving the remote peer's header, the signature is verified (against their supplied public key), and the

event is emitted. A secure-peer client (i.e. the peer that initiated the connection) should now check that they have achived a connection to the peer they expected to connect to. It would be expected for a server to receive connections from potentially unknown peers.

If all the

listeners call
then the connection is accepted, and the secret is derived from the diffie-helman keys.

The cipher is selected by a ordinal voting algorithm. Each peer sends an preference ordered list of supported ciphers. The cipher is picked by selecting the peer with the greatest token, and then using their most preferred token which is also supported by the other peer.

Security Hole (minor): Since a peer can choose their token "randomly" they can choose particularily high tokens that are highly likely to allow them to pick the cipher. This could be used for a cipher downgrade attack... cipher selection is generally problematic... but this weakness is probably best remedied by some out of band way to make sure the network upgrade cycle is short. It might be better to elect the cipher picking peer by a fair method (where the winner is provably fair, such picking the peer who's token is closest to

hash(p1.token + p2.token)
. Another option might be to allow the server (i.e. person who picked up the phone) to select the cipher since they are in a slightly more vulnerable position (and more likely to be the victum in an attack).

Now, stream packets may be sent to the remote peer. Each packet is framed along with an incrementing sequence number, and the token sent by the remote peer, this prevents replay attacks, because the peer will not accept packets with the wrong token, and prevents reordering attacks, since the peer will not accept replayed packets if the sequence numbers are not in order.

Security Hole: Although the session cannot be replayed, the initial handshake can be replayed. This will cause the server to believe it has established a connection, if the attacker does not send any content then the attack will not be discovered. This could probably be used for a denial of service attack, or maybe to cause the server to leak information via timing, in cases where they implement a protocol that streams realtime data without waiting for the client to send anything.


var secure = require('secure-peer')

var peer = secure(keys, opts={})

Return a function to create streams given the


should be a private PEM string and
should be a public PEM string.

You can generate keypairs with rsa-json.

You can set a preference ordering array of ciphers to use with

. Both sides will use a deterministic ordinal voting algorithm to determine which cipher to use. See
openssl list-cipher-algorithms
for the whole list.

var sec = peer(cb)

Create a new duplex stream

that caries the encrypted contents. This stream is safe to stream over the wire, including untrusted networks.

is a shorthand to listen on the
event just like


sec.on('connection', function (stream) {})

Emitted with the decrypted plaintext stream when the secure connection has been established successfully.
is the identify object from the

sec.on('identify', function (id) {})

Emitted when the connection identifies with its public key,


Each listener must call either


The connection won't be accepted until all listeners call

. If any listener calls
, the connection will be aborted.


Accept the connection. This function must be called for every listener on the

event for the connection to succeed.


Reject the connection. The connection will not succeed even if

was called in another listener.

sec.on('header', function (header) {})

Emitted when the remote side provides a signed header.payload json string signed with its private key in header.hash.


With npm do:

npm install secure-peer



We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.