Skip to content

Instantly share code, notes, and snippets.

@shesek
Last active June 26, 2017 00:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save shesek/5847473 to your computer and use it in GitHub Desktop.
Save shesek/5847473 to your computer and use it in GitHub Desktop.
Bitcoin type-2 deterministic keys for nodejs/javascript, implemented in CoffeeScript

Generate deterministic keys based on a master seed passpharse, a private key or a public key.

Each derived key-pair are created based on a string that can be freely chosen (an incremented counter, an order id, user id, or just about anything)

When a private key (or a private key based on a passphrase) is used, the public key and the private key are returned.

When the matching public key is used, only the public key is returned - but its the same public key as the one created from the private key using the same string.

Usage

From seed passphrase:

keygen = deterministic seed: 'my very secret password'
keys = keygen 'payment-1' # { pub, priv, address }

From seed private key:

keygen = deterministic priv: [0xc0, 0x0f, 0xfe, ... (byte array)]
keys = keygen 'change-3' # { pub, priv, address }

From seed pubkey

keygen = deterministic pub: [0xc0, 0x0f, 0xfe, ... (byte array)]
keys = keygen 'order-998181' # { pub, address }

Get the public key of an passphrase:

master = make_master seed: 'my very secret password'
console.log Crypto.util.bytesToBase64 master.pub
# Requires [bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib/)
# (see https://gist.github.com/shesek/5847524 for getting it running under nodejs)
# Released under the MIT license
{ Address } = Bitcoin
{ SHA256 } = Crypto
sha256b = (bytes) -> SHA256 bytes, asBytes: true
lpad = (size, bytes) -> bytes.unshift 0x00 while bytes.length<size; bytes
deterministic = (keys) ->
master = make_master keys
mpk = master.pub.slice(1)
curve = getSECCurveByName 'secp256k1'
pt = ECPointFp.decodeFrom curve.getCurve(), master.pub
N = curve.getN()
(seq) ->
seq_bi = BigInteger.fromByteArrayUnsigned sha256b (sha256b UTF8.stringToBytes seq).concat(mpk)
pub: pub = (pt.add curve.getG().multiply seq_bi).getEncoded()
priv: lpad 32, seq_bi.add(master.secexp).mod(N).toByteArrayUnsigned() if master.priv?
addr: new Address sha256ripe160 pub
make_master = ({ seed, secexp, priv, pub }) ->
priv ?= sha256b sha256b seed if seed?
if priv?
secexp ?= BigInteger.fromByteArrayUnsigned priv
pub ?= get_pubkey secexp
{ seed, secexp, priv, pub }
get_pubkey = (secexp) ->
(getSECCurveByName 'secp256k1')
.getG().multiply(secexp)
.getEncoded()
module.exports = { deterministic, make_master }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment