Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions src/ecdsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ function parseSigCompact(buffer) {
* http://www.secg.org/download/aid-780/sec1-v2.pdf
*/
function recoverPubKey(curve, e, signature, i) {
assert.strictEqual(i & 3, i, 'The recovery param is more than two bits')
assert.strictEqual(i & 3, i, 'Recovery param is more than two bits')

var r = signature.r
var s = signature.s
Expand All @@ -210,35 +210,32 @@ function recoverPubKey(curve, e, signature, i) {
curve.P_OVER_FOUR = p.add(BigInteger.ONE).shiftRight(2)
}

// 1.1 Compute x
// 1.1 Let x = r + jn
var x = isSecondKey ? r.add(n) : r

// 1.3 Convert x to point
// 1.2, 1.3 Convert x to a point R using routine specified in Section 2.3.4
var alpha = x.pow(3).add(a.multiply(x)).add(b).mod(p)
var beta = alpha.modPow(curve.P_OVER_FOUR, p)

// If beta is even, but y isn't, or vice versa, then convert it,
// otherwise we're done and y == beta.
var y = (beta.isEven() ^ isYEven) ? p.subtract(beta) : beta

// 1.4 Check that nR isn't at infinity
// 1.4 Check that nR is at infinity
var R = Point.fromAffine(curve, x, y)
curve.validate(R)
var nR = R.multiply(n)
assert(curve.isInfinity(nR), 'nR is not a valid curve point')

// 1.5 Compute -e from e
// Compute -e from e
var eNeg = e.negate().mod(n)

// 1.6 Compute Q = r^-1 (sR - eG)
// Q = r^-1 (sR + -eG)
// 1.6.1 Compute Q = r^-1 (sR - eG)
// Q = r^-1 (sR + -eG)
var rInv = r.modInverse(n)

var Q = R.multiplyTwo(s, G, eNeg).multiply(rInv)
curve.validate(Q)

if (!verifyRaw(curve, e, signature, Q)) {
throw new Error("Pubkey recovery unsuccessful")
}

return Q
}

Expand All @@ -257,6 +254,7 @@ function calcPubKeyRecoveryParam(curve, e, signature, Q) {
for (var i = 0; i < 4; i++) {
var Qprime = recoverPubKey(curve, e, signature, i)

// 1.6.2 Verify Q
if (Qprime.equals(Q)) {
return i
}
Expand Down
19 changes: 3 additions & 16 deletions src/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ function Transaction(doc) {
this.outs = []

if (doc) {
if (typeof doc == "string" || Array.isArray(doc)) {
doc = Transaction.fromBuffer(doc)
}

if (doc.hash) this.hash = doc.hash;
if (doc.version) this.version = doc.version;
if (doc.locktime) this.locktime = doc.locktime;
Expand Down Expand Up @@ -388,16 +384,9 @@ Transaction.prototype.estimateFee = function(feePerKb){
return feePerKb * Math.ceil(size / 1000)
}

var TransactionIn = function (data) {
if (typeof data == "string") {
this.outpoint = { hash: data.split(':')[0], index: data.split(':')[1] }
} else if (data.outpoint) {
this.outpoint = data.outpoint
} else {
this.outpoint = { hash: data.hash, index: data.index }
}

assert(data.script, 'Invalid TxIn parameters')
function TransactionIn(data) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You changed the constructor here, just wanted to make mention of it incase it was an accident (PR comment didn't mention a TX constructor change).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, this was added after the PR was written, as I continued removing untested functionality.
I will amend the PR.

assert(data.outpoint && data.script, 'Invalid TxIn parameters')
this.outpoint = data.outpoint
this.script = data.script
this.sequence = data.sequence == undefined ? DEFAULT_SEQUENCE : data.sequence
}
Expand All @@ -417,8 +406,6 @@ function TransactionOut(data) {
this.script = data.script
this.value = data.value
this.address = data.address

if (data.address) this.address = data.address
}

TransactionOut.prototype.clone = function() {
Expand Down
52 changes: 46 additions & 6 deletions test/ecdsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,57 @@ describe('ecdsa', function() {
})

describe('recoverPubKey', function() {
it('succesfully recovers a public key', function() {
var d = BigInteger.ONE
var signature = new Buffer('INcvXVVEFyIfHLbDX+xoxlKFn3Wzj9g0UbhObXdMq+YMKC252o5RHFr0/cKdQe1WsBLUBi4morhgZ77obDJVuV0=', 'base64')
fixtures.valid.forEach(function(f) {
it('recovers the pubKey for ' + f.d, function() {
var d = BigInteger.fromHex(f.d)
var Q = curve.params.G.multiply(d)
var signature = {
r: new BigInteger(f.signature.r),
s: new BigInteger(f.signature.s)
}
var h1 = crypto.sha256(f.message)
var e = BigInteger.fromBuffer(h1)
var Qprime = ecdsa.recoverPubKey(curve, e, signature, f.compact.i)

assert(Qprime.equals(Q))
})
})

var Q = curve.params.G.multiply(d)
describe('with i ∈ {0,1,2,3}', function() {
var hash = message.magicHash('1111', networks.bitcoin)
var e = BigInteger.fromBuffer(hash)

var signature = new Buffer('INcvXVVEFyIfHLbDX+xoxlKFn3Wzj9g0UbhObXdMq+YMKC252o5RHFr0/cKdQe1WsBLUBi4morhgZ77obDJVuV0=', 'base64')
var parsed = ecdsa.parseSigCompact(signature)
var points = [
'03e3a8c44a8bf712f1fbacee274fb19c0239b1a9e877eff0075ea335f2be8ff380',
'0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
'03d49e765f0bc27525c51a1b98fb1c99dacd59abe85a203af90f758260550b56c5',
'027eea09d46ac7fb6aa2e96f9c576677214ffdc238eb167734a9b39d1eb4c3d30d'
]

points.forEach(function(expectedHex, i) {
it('recovers an expected point for i of ' + i, function() {
var Qprime = ecdsa.recoverPubKey(curve, e, parsed.signature, i)
var QprimeHex = Qprime.getEncoded().toString('hex')

assert.equal(QprimeHex, expectedHex)
})
})
})

fixtures.invalid.recoverPubKey.forEach(function(f) {
it('throws on ' + f.description, function() {
var e = BigInteger.fromHex(f.e)
var signature = {
r: new BigInteger(f.signature.r),
s: new BigInteger(f.signature.s)
}

var Qprime = ecdsa.recoverPubKey(curve, e, parsed.signature, parsed.i)
assert(Q.equals(Qprime))
assert.throws(function() {
ecdsa.recoverPubKey(curve, e, signature, f.i)
}, new RegExp(f.exception))
})
})
})

Expand Down
22 changes: 22 additions & 0 deletions test/fixtures/ecdsa.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,28 @@
"hex": "300c0204ffffffff0202ffffffff"
}
],
"recoverPubKey": [
{
"description": "Invalid r value (== 0)",
"exception": "nR is not a valid curve point",
"e": "01",
"signature": {
"r": "00",
"s": "02"
},
"i": 0
},
{
"description": "Invalid i value (> 3)",
"exception": "Recovery param is more than two bits",
"e": "01",
"signature": {
"r": "00",
"s": "02"
},
"i": 4
}
],
"verifyRaw": [
{
"description": "The wrong signature",
Expand Down
80 changes: 80 additions & 0 deletions test/fixtures/scripts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"valid": {
"pubKey": [
{
"pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1",
"signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801",
"scriptPubKey": "2102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1ac",
"scriptSig": "47304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801"
}
],
"pubKeyHash": [
{
"pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1",
"signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801",
"scriptPubKey": "76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac",
"scriptSig": "47304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca28012102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1"
}
],
"multisig": [
{
"pubKeys": [
"02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1",
"0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a"
],
"signatures": [
"304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801",
"3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501"
],
"scriptPubKey": "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae",
"scriptSig": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501"
},
{
"pubKeys": [
"02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f",
"02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f",
"036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19"
],
"signatures": [],
"scriptPubKey": "532102ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f2102fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f21036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e1953ae",
"scriptSig": ""
}
],
"scripthash": [
{
"redeemScript": "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae",
"redeemScriptSig": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501",
"scriptSig": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d14050147522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae",
"scriptPubKey": "a914722ff0bc2c3f47b35c20df646c395594da24e90e87"
}
]
},
"invalid": {
"multisig": [
{
"exception": "Not enough pubKeys provided",
"m": 4,
"pubKeys": [
"02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f",
"02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f",
"036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19"
],
"signatures": [
"304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801"
],
"scriptPubKey": true
},
{
"exception": "Not enough signatures provided",
"pubKeys": [
"02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1",
"0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a"
],
"signatures": [
"304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801"
],
"scriptPubKey": false
}
]
}
}
10 changes: 10 additions & 0 deletions test/message.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var assert = require('assert')
var networks = require('../src/networks')

var Address = require('../src/address')
var BigInteger = require('bigi')
var ECKey = require('../src/eckey')
var Message = require('../src/message')
Expand All @@ -20,6 +21,15 @@ describe('Message', function() {
})

describe('verify', function() {
it('accepts an Address object', function() {
var f = fixtures.valid.verify[0]
var network = networks[f.network]

var address = Address.fromBase58Check(f.address)
var signature = new Buffer(f.signature, 'base64')
assert.ok(Message.verify(address, signature, f.message, network))
})

fixtures.valid.verify.forEach(function(f) {
it('verifies a valid signature for \"' + f.message + '\" (' + f.network + ')', function() {
var network = networks[f.network]
Expand Down
Loading