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 🙏

© 2026 – Pkg Stats / Ryan Hefner

multisig-hmac

v0.1.1

Published

.

Readme

multisig-hmac

Multisig scheme for HMAC authentication

Work in progress

Rationale

Many APIs use symmetric "signatures", through HMACs, of a nonce and the data to be processed by a remote server. You always trust the receiving party to process the data, as they are the trusted 3rd party with access to all keys, however the external party, making the call to be processed, only has power over their own keys. As an external party you might want to secure high sensitivity calls with additional checks such as allowed IP ranges. Another check is that multiple parties on the issuing side co-sign the request.

Imagine a withdrawal action on a bank or exchange or a delete call on a cloud provider, which are both highly sensitive, "destructive" actions. Using this multisig scheme, several separate entities on the calling party, will have to agree to perform the action. This could be multiple servers or people storing their own personal credentials.

This scheme takes each separate signature and a bitfield indicating the keys used, which are combinable in an any order, allowing for simple threshold schemes, or more advanced authentication flows.

Signatures made with this scheme are the same size as standard HMACs, with keys being the same size. This module supports the SHA-2 suite of algorithms for HMAC making it backwards compatible, in sizes, with existing HMAC authentication. Implementing this scheme only requires storing the threshold for actions that are multisig enabled.

Usage

Key managment can happen in either of two modes, either by storing every of the component keys, or by storing a single master seed and using that to derive keys ad hoc.

Using stored keys:

const MultisigHMAC = require('multisig-hmac')

const multisigHmac = new MultisigHMAC()

// generate keys, which need to be stored securely
// and need to be shared securely with each party
const k1 = multisigHmac.keygen(1)
const k2 = multisigHmac.keygen(2)
const k3 = multisigHmac.keygen(3)

// Sign by each client with 2-of-3
const data = Buffer.from('Hello world')

// Notice no mention of nonce here. The data can follow whatever format you
// desire, but should include a nonce
const s1 = multisigHmac.sign(k1, data)
const s3 = multisigHmac.sign(k3, data)

const signature = multisigHmac.combine([s1, s3])

// Verify on the server
const threshold = 2
const keys = [k1, k2, k3]
const verified = multisigHmac.verify(keys, signature, data, threshold)
console.log(verified)

Using a derived master key:

const MultisigHMAC = require('multisig-hmac')

const multisigHmac = new MultisigHMAC()

// Generate a master seed, which needs to be stored securely
// This seed must NOT be shared with any other party
const seed = multisigHmac.seedgen()

const k1 = multisigHmac.deriveKey(seed, 1)
const k2 = multisigHmac.deriveKey(seed, 2)
const k3 = multisigHmac.deriveKey(seed, 3)

// Sign by each client with 2-of-3
const data = Buffer.from('Hello world')

// Notice no mention of nonce here. The data can follow whatever format you
// desire, but should include a nonce
const s1 = multisigHmac.sign(k1, data)
const s3 = multisigHmac.sign(k3, data)

const signature = multisigHmac.combine([s1, s3])

// Verify on the server, but now keys are dynamically derived
const threshold = 2
const verified = multisigHmac.verifyDerived(seed, signature, data, threshold)
console.log(verified)

API

Constants

  • MultisigHMAC.BYTES signature length in bytes (default)
  • MultisigHMAC.KEYBYTES key length in bytes (default)
  • MultisigHMAC.PRIMITIVE is sha256 (default)

Specific algorithms (support depends on your OpenSSL version):

  • MultisigHMAC.SHA256_BYTES signature length in bytes
  • MultisigHMAC.SHA256_KEYBYTES key length in bytes
  • MultisigHMAC.SHA256_PRIMITIVE is sha256
  • MultisigHMAC.SHA384_BYTES signature length in bytes
  • MultisigHMAC.SHA384_KEYBYTES key length in bytes
  • MultisigHMAC.SHA384_PRIMITIVE is sha384
  • MultisigHMAC.SHA512_BYTES signature length in bytes
  • MultisigHMAC.SHA512_KEYBYTES key length in bytes
  • MultisigHMAC.SHA512_PRIMITIVE is sha512
  • MultisigHMAC.SHA512_256_BYTES signature length in bytes
  • MultisigHMAC.SHA512_256_KEYBYTES key length in bytes
  • MultisigHMAC.SHA512_256_PRIMITIVE is sha512_256 (also knowns as SHA512/256)

const n = MultisigHMAC.keysCount(bitfield)

Returns the number of keys (ie. high bits) in bitfield. bitfield must be a uint32.

Example: assert(MultisigHMAC.keyIndexes(0b101), 2)

const xs = MultisigHMAC.keyIndexes(bitfield)

Returns the indexes of the keys (ie. high bits) in bitfield as an array. bitfield must be a uint32.

Example: assert(MultisigHMAC.keyIndexes(0b101), [1, 3])

const multisigHmac = new MultisigHMAC([alg = MultisigHMAC.PRIMITIVE])

Create a new instance of MultisigHMAC, which can be used as a global singleton. Just sets the algorithm to be used for subsequent methods and associated constants.

const key = multisigHmac.keygen(index, [buf])

Generate a new cryptographically random key. Optionally pass a Buffer of length KEYBYTES that the key will be written to. This will then be the same Buffer in key.key. Returns { index: uint32, key: Buffer }.

Note: index should be counted from 0

const masterSeed = multisigHmac.seedgen([buf])

Generate a new cryptographically random master seed. Optionally pass a Buffer of length KEYBYTES that the seed will be written to. This will then be the same Buffer returned.

const key = multisigHmac.deriveKey(masterSeed, index, [buf])

Derive a new sub key from a master seed. index must be a uint32, but in practice you want to keep a much lower number, as the bitfield used with the signature has as many bits as the largest index. A simple counter suffices. Optionally pass a Buffer of length KEYBYTES that the key will be written to. This will then be the same Buffer in key.key. Returns { index: uint32, key: Buffer }

Note: index should be counted from 0

Keys are derived using a KDF based on HMAC:

 b[0...BYTES] = HMAC(Key = masterSeed, data = 'derive' || U32LE(index) || 0x00)
 b[BYTES...] = HMAC(Key = masterSeed, b[0...BYTES] || 0x01)

const signature = multisigHmac.sign(key, data, [buf])

Independently sign Buffer data with key, using the optional buf to store the signature. buf must be at least BYTES long. Returns { bitfield: uint32, signature: Buffer }. This object can be passed to combine()

const signature = multisigHmac.combine([ signatures... ], [buf])

Combine a list of signatures, which have all been signed independently. Only include each signature once or they will cancel out. Optionally pass buf, which will store the aggregate signature. This must be a Buffer of BYTES. Signatures can be combined in any order and over several passes for more advanced aggregation schemes. Returns { bitfield: uint32, signature: Buffer }

const valid = multisigHmac.verify(keys, signature, data, threshold, [sigScratchBuf])

Verify a signature of data against a list of keys, over a given threshold. keys must be an Array of keys, from which the signature.bitfield defines which must be verified. Optionally pass sigScratchBuf which will be used for intermediate signature verification. This Buffer must be BYTES long. Returns a Boolean for success.

const valid = multisigHmac.verifyDerived(masterSeed, signature, data, threshold, [keyScratchBuf], [sigScratchBuf])

Verify a signature of data against dynamically derived keys from masterSeed, over a given threshold. masterSeed must be an Buffer of length KEYBYTES, from which the signature.bitfield defines which must be derived and verified. Optionally pass keyScratchBuf for which the intermediate keys are derived into and sigScratchBuf which will be used for intermediate signature verification. These Buffers must be KEYBYTES and BYTES long, respectively. Returns a Boolean for success.

Install

npm install multisig-hmac

License

ISC