diff --git a/test/integration/stealth.js b/test/integration/stealth.js index 7808d54..5c0fa9e 100644 --- a/test/integration/stealth.js +++ b/test/integration/stealth.js @@ -12,10 +12,8 @@ var n = secp256k1.n // vG = (dG * sha256(e * dG)G) function stealthSend (e, Q) { var eQ = Q.multiply(e) // shared secret - var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) var cG = G.multiply(c) - var vG = new bitcoin.ECPair(null, Q.add(cG)) return vG @@ -24,7 +22,6 @@ function stealthSend (e, Q) { // v = (d + sha256(eG * d)) function stealthReceive (d, eG) { var eQ = eG.multiply(d) // shared secret - var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) var v = new bitcoin.ECPair(d.add(c).mod(n)) @@ -40,6 +37,34 @@ function stealthRecoverLeaked (v, e, Q) { return d } +// vG = (rG * (dG * sha256(e * dG)G)) +function stealthDualSend (e, R, Q) { + var eQ = Q.multiply(e) // shared secret + var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) + var cG = G.multiply(c) + var vG = new bitcoin.ECPair(null, R.add(Q.add(cG))) + + return vG +} + +// vG = (rG * (d + sha256(eG * d))G) +function stealthDualScan (d, R, eG) { + var eQ = eG.multiply(d) // shared secret + var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) + var vG = new bitcoin.ECPair(null, R.add(G.multiply(d.add(c).mod(n)))) + + return vG +} + +// v = (r + d + sha256(eG * d)) +function stealthDualReceive (d, r, eG) { + var eQ = eG.multiply(d) // shared secret + var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) + var v = new bitcoin.ECPair(r.add(d).add(c).mod(n)) + + return v +} + describe('bitcoinjs-lib (crypto)', function () { it('can generate a single-key stealth address', function () { // XXX: should be randomly generated, see next test for example @@ -93,6 +118,26 @@ describe('bitcoinjs-lib (crypto)', function () { assert.equal(leaked.toWIF(), recipient.toWIF()) }) - // TODO - it.skip('can generate a dual-key stealth address', function () {}) + it('can generate a dual-key stealth address (randomly)', function () { + // XXX: should be randomly generated, see next test for example + var recipient = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') // private to recipient + var scan = bitcoin.ECPair.fromWIF('L5DkCk3xLLoGKncqKsWQTdaPSR4V8gzc14WVghysQGkdryRudjBM') // private to scanner/recipient + var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender + + // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender + var forSender = stealthDualSend(nonce.d, recipient.Q, scan.Q) + assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) + + // ... sender reveals nonce public key (nonce.Q) to scanner + var forScanner = stealthDualScan(scan.d, recipient.Q, nonce.Q) + assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) + + // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient + var forRecipient = stealthDualReceive(scan.d, recipient.d, nonce.Q) + assert.doesNotThrow(function () { forRecipient.toWIF() }) + + // scanner, sender and recipient, all derived same address + assert.equal(forSender.getAddress(), forScanner.getAddress()) + assert.equal(forSender.getAddress(), forRecipient.getAddress()) + }) })