2014-10-17 04:31:01 +02:00
|
|
|
/* global describe, it, beforeEach */
|
|
|
|
/* eslint-disable no-new */
|
|
|
|
|
|
|
|
var assert = require('assert')
|
|
|
|
var ecdsa = require('../src/ecdsa')
|
|
|
|
var ecurve = require('ecurve')
|
|
|
|
var proxyquire = require('proxyquire')
|
|
|
|
var sinon = require('sinon')
|
|
|
|
|
|
|
|
var BigInteger = require('bigi')
|
|
|
|
var ECPair = require('../src/ecpair')
|
|
|
|
|
|
|
|
var fixtures = require('./fixtures/ecpair.json')
|
2015-09-08 15:44:37 +02:00
|
|
|
var curve = ecdsa.__curve
|
2014-10-17 04:31:01 +02:00
|
|
|
|
2015-07-28 09:47:18 +02:00
|
|
|
var NETWORKS = require('../src/networks')
|
2015-07-28 08:42:57 +02:00
|
|
|
var NETWORKS_LIST = [] // Object.values(NETWORKS)
|
|
|
|
for (var networkName in NETWORKS) {
|
|
|
|
NETWORKS_LIST.push(NETWORKS[networkName])
|
|
|
|
}
|
|
|
|
|
2014-10-17 04:31:01 +02:00
|
|
|
describe('ECPair', function () {
|
|
|
|
describe('constructor', function () {
|
|
|
|
it('defaults to compressed', function () {
|
|
|
|
var keyPair = new ECPair(BigInteger.ONE)
|
|
|
|
|
2015-05-07 03:29:20 +02:00
|
|
|
assert.strictEqual(keyPair.compressed, true)
|
2014-10-17 04:31:01 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
it('supports the uncompressed option', function () {
|
|
|
|
var keyPair = new ECPair(BigInteger.ONE, null, {
|
|
|
|
compressed: false
|
|
|
|
})
|
|
|
|
|
2015-05-07 03:29:20 +02:00
|
|
|
assert.strictEqual(keyPair.compressed, false)
|
2014-10-17 04:31:01 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
it('supports the network option', function () {
|
|
|
|
var keyPair = new ECPair(BigInteger.ONE, null, {
|
|
|
|
compressed: false,
|
2015-07-28 08:42:57 +02:00
|
|
|
network: NETWORKS.testnet
|
2014-10-17 04:31:01 +02:00
|
|
|
})
|
|
|
|
|
2015-07-28 08:42:57 +02:00
|
|
|
assert.strictEqual(keyPair.network, NETWORKS.testnet)
|
2014-10-17 04:31:01 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
fixtures.valid.forEach(function (f) {
|
|
|
|
it('calculates the public point for ' + f.WIF, function () {
|
|
|
|
var d = new BigInteger(f.d)
|
|
|
|
var keyPair = new ECPair(d, null, {
|
|
|
|
compressed: f.compressed
|
|
|
|
})
|
|
|
|
|
2015-05-07 03:29:20 +02:00
|
|
|
assert.strictEqual(keyPair.getPublicKeyBuffer().toString('hex'), f.Q)
|
2014-10-17 04:31:01 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
fixtures.invalid.constructor.forEach(function (f) {
|
2015-09-12 07:17:45 +02:00
|
|
|
it('throws ' + f.exception, function () {
|
|
|
|
var d = f.d && new BigInteger(f.d)
|
|
|
|
var Q = f.Q && ecurve.Point.decodeFrom(curve, new Buffer(f.Q, 'hex'))
|
2014-10-17 04:31:01 +02:00
|
|
|
|
|
|
|
assert.throws(function () {
|
2015-09-12 07:17:45 +02:00
|
|
|
new ECPair(d, Q, f.options)
|
2014-10-17 04:31:01 +02:00
|
|
|
}, new RegExp(f.exception))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('getPublicKeyBuffer', function () {
|
|
|
|
var keyPair
|
|
|
|
|
|
|
|
beforeEach(function () {
|
|
|
|
keyPair = new ECPair(BigInteger.ONE)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('wraps Q.getEncoded', sinon.test(function () {
|
|
|
|
this.mock(keyPair.Q).expects('getEncoded')
|
2015-09-08 15:44:37 +02:00
|
|
|
.once().withArgs(keyPair.compressed)
|
2014-10-17 04:31:01 +02:00
|
|
|
|
|
|
|
keyPair.getPublicKeyBuffer()
|
|
|
|
}))
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('fromWIF', function () {
|
|
|
|
fixtures.valid.forEach(function (f) {
|
2015-07-28 08:42:57 +02:00
|
|
|
it('imports ' + f.WIF + ' (' + f.network + ')', function () {
|
|
|
|
var network = NETWORKS[f.network]
|
|
|
|
var keyPair = ECPair.fromWIF(f.WIF, network)
|
|
|
|
|
|
|
|
assert.strictEqual(keyPair.d.toString(), f.d)
|
|
|
|
assert.strictEqual(keyPair.compressed, f.compressed)
|
|
|
|
assert.strictEqual(keyPair.network, network)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
fixtures.valid.forEach(function (f) {
|
|
|
|
it('imports ' + f.WIF + ' (via list of networks)', function () {
|
|
|
|
var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
|
2014-10-17 04:31:01 +02:00
|
|
|
|
2015-05-07 03:29:20 +02:00
|
|
|
assert.strictEqual(keyPair.d.toString(), f.d)
|
|
|
|
assert.strictEqual(keyPair.compressed, f.compressed)
|
2015-07-28 08:42:57 +02:00
|
|
|
assert.strictEqual(keyPair.network, NETWORKS[f.network])
|
2014-10-17 04:31:01 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
fixtures.invalid.fromWIF.forEach(function (f) {
|
2015-08-20 12:16:57 +02:00
|
|
|
it('throws on ' + f.WIF, function () {
|
2014-10-17 04:31:01 +02:00
|
|
|
assert.throws(function () {
|
2016-02-25 03:26:05 +01:00
|
|
|
var networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST
|
|
|
|
|
|
|
|
ECPair.fromWIF(f.WIF, networks)
|
2014-10-17 04:31:01 +02:00
|
|
|
}, new RegExp(f.exception))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('toWIF', function () {
|
|
|
|
fixtures.valid.forEach(function (f) {
|
2015-07-28 08:42:57 +02:00
|
|
|
it('exports ' + f.WIF, function () {
|
|
|
|
var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
|
2014-10-17 04:31:01 +02:00
|
|
|
var result = keyPair.toWIF()
|
|
|
|
|
2015-05-07 03:29:20 +02:00
|
|
|
assert.strictEqual(result, f.WIF)
|
2014-10-17 04:31:01 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('makeRandom', function () {
|
|
|
|
var d = new Buffer('0404040404040404040404040404040404040404040404040404040404040404', 'hex')
|
|
|
|
var exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv'
|
|
|
|
|
|
|
|
describe('uses randombytes RNG', function () {
|
|
|
|
it('generates a ECPair', function () {
|
|
|
|
var stub = { randombytes: function () { return d } }
|
|
|
|
var ProxiedECPair = proxyquire('../src/ecpair', stub)
|
|
|
|
|
|
|
|
var keyPair = ProxiedECPair.makeRandom()
|
2015-05-07 03:29:20 +02:00
|
|
|
assert.strictEqual(keyPair.toWIF(), exWIF)
|
2014-10-17 04:31:01 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
it('allows a custom RNG to be used', function () {
|
|
|
|
var keyPair = ECPair.makeRandom({
|
|
|
|
rng: function (size) { return d.slice(0, size) }
|
|
|
|
})
|
|
|
|
|
2015-05-07 03:29:20 +02:00
|
|
|
assert.strictEqual(keyPair.toWIF(), exWIF)
|
2014-10-17 04:31:01 +02:00
|
|
|
})
|
2015-09-08 15:39:31 +02:00
|
|
|
|
2016-10-10 04:01:51 +02:00
|
|
|
it('retains the same defaults as ECPair constructor', function () {
|
|
|
|
var keyPair = ECPair.makeRandom()
|
|
|
|
|
|
|
|
assert.strictEqual(keyPair.compressed, true)
|
|
|
|
assert.strictEqual(keyPair.network, NETWORKS.bitcoin)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('supports the options parameter', function () {
|
|
|
|
var keyPair = ECPair.makeRandom({
|
|
|
|
compressed: false,
|
|
|
|
network: NETWORKS.testnet
|
|
|
|
})
|
|
|
|
|
|
|
|
assert.strictEqual(keyPair.compressed, false)
|
|
|
|
assert.strictEqual(keyPair.network, NETWORKS.testnet)
|
|
|
|
})
|
|
|
|
|
2016-12-13 23:42:21 +01:00
|
|
|
it('loops until d is within interval [1, n - 1] : 1', sinon.test(function () {
|
|
|
|
var rng = this.mock()
|
|
|
|
rng.exactly(2)
|
|
|
|
rng.onCall(0).returns(BigInteger.ZERO.toBuffer(32)) // invalid length
|
|
|
|
rng.onCall(1).returns(BigInteger.ONE.toBuffer(32)) // === 1
|
|
|
|
|
|
|
|
ECPair.makeRandom({ rng: rng })
|
|
|
|
}))
|
|
|
|
|
|
|
|
it('loops until d is within interval [1, n - 1] : n - 1', sinon.test(function () {
|
2015-09-08 15:39:31 +02:00
|
|
|
var rng = this.mock()
|
|
|
|
rng.exactly(3)
|
2016-12-13 23:42:21 +01:00
|
|
|
rng.onCall(0).returns(BigInteger.ZERO.toBuffer(32)) // < 1
|
2015-09-08 15:39:31 +02:00
|
|
|
rng.onCall(1).returns(curve.n.toBuffer(32)) // > n-1
|
2016-12-13 23:42:21 +01:00
|
|
|
rng.onCall(2).returns(curve.n.subtract(BigInteger.ONE).toBuffer(32)) // === n-1
|
2015-09-08 15:39:31 +02:00
|
|
|
|
|
|
|
ECPair.makeRandom({ rng: rng })
|
|
|
|
}))
|
2014-10-17 04:31:01 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
describe('getAddress', function () {
|
|
|
|
fixtures.valid.forEach(function (f) {
|
|
|
|
it('returns ' + f.address + ' for ' + f.WIF, function () {
|
2015-07-28 08:42:57 +02:00
|
|
|
var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
|
2014-10-17 04:31:01 +02:00
|
|
|
|
2015-07-08 07:56:21 +02:00
|
|
|
assert.strictEqual(keyPair.getAddress(), f.address)
|
2014-10-17 04:31:01 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2015-09-21 09:37:21 +02:00
|
|
|
describe('getNetwork', function () {
|
|
|
|
fixtures.valid.forEach(function (f) {
|
|
|
|
it('returns ' + f.network + ' for ' + f.WIF, function () {
|
|
|
|
var network = NETWORKS[f.network]
|
|
|
|
var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
|
|
|
|
|
|
|
|
assert.strictEqual(keyPair.getNetwork(), network)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2014-10-17 04:31:01 +02:00
|
|
|
describe('ecdsa wrappers', function () {
|
|
|
|
var keyPair, hash
|
|
|
|
|
|
|
|
beforeEach(function () {
|
|
|
|
keyPair = ECPair.makeRandom()
|
|
|
|
hash = new Buffer(32)
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('signing', function () {
|
|
|
|
it('wraps ecdsa.sign', sinon.test(function () {
|
|
|
|
this.mock(ecdsa).expects('sign')
|
2015-09-08 15:44:37 +02:00
|
|
|
.once().withArgs(hash, keyPair.d)
|
2014-10-17 04:31:01 +02:00
|
|
|
|
|
|
|
keyPair.sign(hash)
|
|
|
|
}))
|
|
|
|
|
|
|
|
it('throws if no private key is found', function () {
|
|
|
|
keyPair.d = null
|
|
|
|
|
|
|
|
assert.throws(function () {
|
|
|
|
keyPair.sign(hash)
|
|
|
|
}, /Missing private key/)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('verify', function () {
|
|
|
|
var signature
|
|
|
|
|
|
|
|
beforeEach(function () {
|
|
|
|
signature = keyPair.sign(hash)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('wraps ecdsa.verify', sinon.test(function () {
|
|
|
|
this.mock(ecdsa).expects('verify')
|
2015-09-08 15:44:37 +02:00
|
|
|
.once().withArgs(hash, signature, keyPair.Q)
|
2014-10-17 04:31:01 +02:00
|
|
|
|
|
|
|
keyPair.verify(hash, signature)
|
|
|
|
}))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|