npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

secret-handshake-over-hypercore

v0.1.7

Published

Tarr's Secret Handshake over a shared encrypted Hypercore feed

Downloads

18

Readme

secret-handshake-over-hypercore

A variant of Tarr's Secret Handshake over a shared Hypercore feed to create secure ephemeral authenticated channels based on capabilities.

Installation

$ npm install secret-handshake-over-hypercore

Usage

const shh = require('secret-handshake-over-hypercore')
// connect to peer
shh.connect(sharedKey, opts)
// generate a key pair
shh.keygen() // returns publicKey and secretKey pair
// create capability
shh.capability('my capability') // returns 32 byte BLAKE2b hash of 'my capability' (without white space)
// or a keyed capability
shh.capability('my capability', publicKey)

Example

The example below demonstrates a connection between a client and a server where read and write capabilities are required. If both parties do not have an equal intersection of capabilities, then authentication will fail and the streams will connect.

Server

const shh = require('secret-handshake-over-hypercore')
const net = require('net')

const sharedKey = Buffer.from('12abf5a9165201b0d5f284d7d902f57b19ca0a6f974bcd8fcc3162c93b2b75f1', 'hex')
const server = net.createServer((socket) => {
  const stream = shh.connect(sharedKey, {
    stream: socket,
    capabilities: [
      shh.capability('read'),
      shh.capability('write'),
    ]
  })

  stream.on('handshake', () => {
    stream.write('hello')
  })
})

server.listen(3000)

Client

const shh = require('secret-handshake-over-hypercore')
const sharedKey = Buffer.from('12abf5a9165201b0d5f284d7d902f57b19ca0a6f974bcd8fcc3162c93b2b75f1', 'hex')
const stream = shh.connect(sharedKey, {
  connect() { return new.connect(3000) },
  capabilities: [
    shh.capability('write'),
    shh.capability('read'),
  ]
})

stream.on('handshake', () => {
  stream.write('hello')
})

How?

This module uses a shared key to create a hypercore replication feed that is read only. This feed is the initial feed used to create a secure channel for sender and receiver hypercore feeds that both sides of the connection have to read from and write to. These hypercore feeds are used to exchange enough messages using a secret handshake to create a shared secret key. The sender and receiver feeds are re-established with session key pairs and the shared secret key is used to box messages that are written to both sides. The initial feed's public key is a BLAKE2b hash of the shared key. The shared key is never shared.

API

shh.keygen()

Generate an ed25519 key pair. The publicKey is suitable for use as a shared key and can be delegated securely to interested parties.

const { publicKey, secretKey } = shh.keygen()

stream = shh.connect(sharedKey[, opts])

Creates an encrypted connection for a sharedKey over a stream connected to Duplex stream like net.Socket.

const stream = shh.connect(sharedKey, {
  connect() {
    return net.connect(3000) // connect to localhost:3000
  }
})

Where

  • sharedKey is a 32 byte buffer containing a unique shared secret key. Both parties must have this key
  • opts is the same as Connection options

capability = shh.capability(capabilityType, [, key])

Creates a 32 byte BLAKE2b hash of capabilityType keyed with an optional 32 byte buffer key. See Connection Capablities.

const { publicKey, secretKey } = shh.keygen()
const capabilities = [
  shh.capability('auth', publicKey),
  shh.capability('read', publicKey),
  shh.capability('write', publicKey),
]

connection = new shh.Connection(opts)

The Connection class is a Duplex stream that wraps the handshake and hypercore feeds used to establish a secure channel. It can be written to and read from safely after the 'handshake' event has been emitted.

The constructor accepts a single argument opts where:

  • opts.sharedKey A required 32 byte buffer containing a unique shared secret key.
  • opts.publicKey An optional public key for the identity initiating the connection
  • opts.secretKey An optional secret key for the identity initiating the connection that is associated with opts.publicKey
  • opts.connect An optional function that should return a Duplex stream to create a connection to the underlying hypercore replication stream
  • opts.stream An optional stream that takes the place of the return value of opts.connect()
  • opts.storage An optional factory function that accepts a connection feed type and returns a random-access-storage interface that will be given to hypercore(...)
  • opts.preserveReceiver An optional boolean to indicate that the connection should preserve the receiver (Connection.RX) feed messages written to it. (Default: false)
  • opts.preserveSender An optional boolean to indicate that the connection should preserve the sender (Connection.TX) feed messages written to it. (Default: false)
  • opts.capabilities An optional array of capabilities that you express and require the connecting party to also provide. See Connection Capabilities

connection.encryptionKey

The initial shared encryption key used to encrypt messages in the sender feed. This property is set after the 'handshake' event is emitted.

connection.hasHandshake

A predicate boolean to indicate if the connection has completed the handshake. This should be true after the 'handshake' event is emitted.

connection.connecting

A predicate boolean to indicate if the connection is currently in a connecting phase. This is set to true when connection.connect() is called and set to false right before the 'connect' event.

connection.connected

A predicate boolean to indicate if the connection is currently connected to another party. This is set to true right before the 'connect' event is emitted.

connection.reading

A predicate boolean to indicate if the connection is currently reading from the other party.

connection.on('handshake', connection)

Emitted when the connection establishes a handshake and can be written to and read from

connection.on('connect', stream, info)

Emitted when the connection connects to the remote where stream is the top level replication feed and info contains

  • info.publicKey The connections identity public key
  • info.sessionPublicKey The ephemeral public key for the connection session
  • info.remotePublicKey The remotes identity public key
  • info.discoveryKey The hypercore discovery key for the initial remote feed

connection.on(Connection.FEED, feed)

Emitted when the top level hypercore feed is ready.

connection.on(Connection.TXI, sender)

Emitted when the initial sender hypercore feed is ready.

connection.on(Connection.RXI, receiver)

Emitted when the initial receiver hypercore feed is ready.

connection.on(Connection.TX, sender)

Emitted when the session sender hypercore feed is ready.

connection.on(Connection.RX, receiver)

Emitted when the session receiver hypercore feed is ready.

connection.on('hello', info)

Emitted when the connection receives a hello from the other party where info contain:

  • info.remoteSessionPublicKey The remotes ephemeral public key for the session
  • info.remotePublicKey The remotes identity public key

connection.on('auth', info)

Emitted when the connection has authenticated with the other party where info contains:

  • info.sessionCapabilities An intersection of the capabilities given by both parties for the connection session
  • info.remoteSessionPublicKey The remotes ephemeral public key for the session
  • info.remotePublicKey The remotes identity public key
  • info.signature The signature of the authentication proof
  • info.proof The authentication proof

connection.on('error', err)

Emitted when a error occurs in the connection.

Connection Feed Types

The connection established over the stream uses several hypercore feeds to ensure a secure channel between both parties. The types of feeds used are represented as string constants and are given to the opts.storage(feedType) factory function, if supplied by the user. This is useful if you want to provide your own storage backend.

Connection.FEED

The top level hypercore feed established for a secure symmetric channel. This feed is never written to.

Connection.FEED = 'feed'

Connection.TXI

The initial sender hypercore feed that both parties establish as an initial feed to write to that the other party will read from. This feed is used for the handshake and then destroyed before establishing a session sender feed. This feed is represented as the Connection.RXI feed on the other end.

Connection.TXI = 'txi'

Connection.RXI

The initial receiver hypercore feed that both parties establish as an initial feed to read from that the other party will write to. This feed is used for the handshake and then destroyed before establishing a session receiver feed. This feed is represented as the Connection.TXI feed on the other end.

Connection.RXI = 'rxi'

Connection.TX

The ephemeral sender hypercore feed that both parties establish after a handshake. Messages written to this feed are encrypted with a unique key derived from a shared encryption key, the current message counter, and an incremented nonce making each message encrypted with a unique key. This feed is both readable and writable.

Connection.TX = 'tx'

Connection.RX

The ephemeral receiver hypercore feed that both parties establish after a handshake. Messages written to this feed are encrypted with a unique key derived from a shared encryption key, the current message counter, and an incremented nonce making each message encrypted with a unique key. This feed is readable only.

Connection.RX = 'rx'

Connection Capabilities

Connection capabilities provide a way to require extra information both parties in the handshake need to know. When given, the handshake requires that both parties share at least one capability during authentication. The intersection of the capabilities between two parties is used as an operand for deriving the initial encryption key used to encrypt messages in the connection.

Capabilities are created with the shh.capability() function. Capabilities are 32 byte BLAKE2b hashes of the input given to the function.

> shh.capability('auth')
<Buffer f4 77 ca 2d 48 ad 0a 39 77 8a f2 86 e1 2b 90 8d a8 53 12 cb 0b e4 e1 f8 c8 eb c0 fd 2e d4 1c af>

Capabilities are given to shh.connect() or new Connection(). If capabilities are not desired then both parties should omit them.

License

MIT