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

@hyperdivision/shielded-multisig

v2.1.0

Published

Shielded Bitcoin Multisig using Public Key tweaking

Downloads

15

Readme

@hyperdivision/shielded-multisig

Build Status

Shielded Bitcoin Multisig using Public Key Tweaking

Create multisig wallets based on a set of public keys without access to secret keys or revealing public keys.

Usage

const shield = require('@hyperdivision/shielded-multisig')

// secp256k1 master keys used to redeem. Here we construct a 2-of-3 address
const threshold = 2
const masterKeys = [
  Buffer.from('...', 'hex'),
  Buffer.from('...', 'hex'),
  Buffer.from('...', 'hex')
]

// id must be 32-byte Buffer.
// Could for example be a 256 bit hash of some user string
const id = Buffer.alloc(32)
id.writeUInt32LE(1) // Just use the Uint32LE value of `1` for this example

// Save the tweak somewhere, and give out the hash. The `counter` may be
// incremented internally if a specific value causes an invalid tweaked key
const { tweakData, address } = shield.address({ id, counter: 0 }, masterKeys, threshold)

// deepEqual(tweakDataS, tweakData) === true
const { tweakData: tweakDataS, script } = shield.redeemScript(tweakData, masterKeys, threshold)

Signing using bcoin:

const shield = require('@hyperdivision/shielded-multisig')
const { tweakPrivate } = require('@hyperdivision/shielded-multisig/tweak')

const threshold = 2
const masterKeys = [
  Buffer.from('...', 'hex'),
  Buffer.from('...', 'hex'),
  Buffer.from('...', 'hex')
]

const { tweakData, script } = shield.redeemScript({ /* ... */ }, masterKeys, threshold)

const privateKey = Buffer.from('...', 'hex')

// bcoin part
const { MTX, KeyRing } = require('bcoin')
const spend = MTX.fromJSON(/* transaction data */)

const ring = KeyRing.fromPrivate(tweakPrivate(tweakData, privateKey))
ring.witness = true
ring.script = script

var signed = spend.sign(ring)
if (signed !== spend.inputs.length) throw new Error('Did not sign all inputs')

// now spend is signed

API

const { tweakData, address } = shield.address({ id, counter }, masterKeys, threshold)

Generate a new address from { id, counter }, masterKeys and threshold. The inputs uniquely and deterministically determine the address.

id must be a 32-byte Buffer and could be eg. a username, account number, persisted random buffer or hash of some user information. The counter can be used to generate multiple addresses for a single user or can be left at 0. Note that tweakData may return a counter different from the one passed, if the details of the algorithm results in an invalid public key. This is extremely rare, but do not rely on the passed counter actually being the one that is used.

masterKeys must be an array of valid secp256k1 public keys encoded as Buffers. Threshold must be an integer less than or equal to the number of master keys.

Returns a tweakData object, that you should persist for future use, and address which is a bcoin Address of a P2SH(P2WSH(m of n)) script that can be encoded as desired.

const { tweakData, script } = shield.redeemScript({ id, counter }, masterKeys, threshold)

Exactly the same as above, but return a bcoin Script instead of an Address.

const areValid = shield.validateMasterkeys(masterKeys)

Validate masterKeys for being points on the curve (ie valid keys)

const tweakedPublicKey = algorithm.tweakPublic(tweakData, publicKey)

Low-level facility to compute the tweaked public key. Note that you must provide the full tweakData as returned by the above functions. Returns a Buffer

const tweakedPrivateKey = algorithm.tweakPrivate(tweakData, privateKey)

Low-level facility to compute the tweaked private key. Note that you must provide the full tweakData as returned by the above functions. Returns a Buffer

const tweak = algorithm.tweak(tweakData)

Compute a non-reduced scalar from tweakData as a 32 byte Buffer. Used internally by tweakPublic and tweakPrivate

Algorithm

This module uses key tweaking based on Diffie-Hellman and hash functions. A given master key pair is tweaked using a hash of tweakData, which works as follows:

  1. The tweakData is hashed using keyed BLAKE2b, as follows tweak = BLAKE2b-256( 32-byte id || U32LE(counter) || U8(threshold), key = nonce). The nonce is computed as the hash of the set of master keys sorted lexicographically, and then concatenated: nonce = BLAKE2b-256(CONCAT(SORT(masterKeys)))
  2. The tweaked key pair can now be computed as PK' = [tweak]·PK and SK' = tweak * SK, where [x]·E denotes scalar multiplication of group element E with scalar x, and x * y denotes field multiplication. The reason this works is because secp256k1 is a prime order group, so every element of the group is a generator. Private/Secret keys are simply scalars over the finite field, while public keys are PK = [SK]·G, where G is the predefined base point of the group. However since any element of the group is a generator we can use the public key as a generator for a new derived key. Since scalar multiplication is associative, [tweak]·[SK]·G = [tweak * SK]·G, so we can recover the derived private key when we need to sign with for the tweaked public key, by applying our tweak to our private key.
  3. If the above [tweak]·PK results in the invalid public key we increment the counter from step 1 and try again.

Install

npm install @hyperdivision/shielded-multisig

License

ISC