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
32 changes: 26 additions & 6 deletions src/ecsignature.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
var assert = require('assert')

var BigInteger = require('bigi')

function ECSignature(r, s) {
Expand Down Expand Up @@ -34,28 +33,49 @@ ECSignature.parseCompact = function(buffer) {
ECSignature.fromDER = function(buffer) {
assert.equal(buffer.readUInt8(0), 0x30, 'Not a DER sequence')
assert.equal(buffer.readUInt8(1), buffer.length - 2, 'Invalid sequence length')

assert.equal(buffer.readUInt8(2), 0x02, 'Expected a DER integer')

var rLen = buffer.readUInt8(3)
var rB = buffer.slice(4, 4 + rLen)
assert(rLen > 0, 'R length is zero')

var offset = 4 + rLen
assert.equal(buffer.readUInt8(offset), 0x02, 'Expected a DER integer (2)')
var sLen = buffer.readUInt8(1 + offset)
var sB = buffer.slice(2 + offset)

var sLen = buffer.readUInt8(offset + 1)
assert(sLen > 0, 'S length is zero')

var rB = buffer.slice(4, offset)
var sB = buffer.slice(offset + 2)
offset += 2 + sLen

if (rLen > 1 && rB.readUInt8(0) === 0x00) {
assert(rB.readUInt8(1) & 0x80, 'R value excessively padded')
}

if (sLen > 1 && sB.readUInt8(0) === 0x00) {
assert(sB.readUInt8(1) & 0x80, 'S value excessively padded')
}

assert.equal(offset, buffer.length, 'Invalid DER encoding')
var r = BigInteger.fromDERInteger(rB)
var s = BigInteger.fromDERInteger(sB)

assert(r.signum() >= 0, 'R value is negative')
assert(s.signum() >= 0, 'S value is negative')

return new ECSignature(r, s)
}

// FIXME: 0x00, 0x04, 0x80 are SIGHASH_* boundary constants, importing Transaction causes a circular dependency
ECSignature.parseScriptSignature = function(buffer) {
var hashType = buffer.readUInt8(buffer.length - 1)
var hashTypeMod = hashType & ~0x80

assert(hashTypeMod > 0x00 && hashTypeMod < 0x04, 'Invalid hashType')

return {
signature: ECSignature.fromDER(buffer.slice(0, -1)),
hashType: buffer.readUInt8(buffer.length - 1)
hashType: hashType
}
}

Expand Down
29 changes: 29 additions & 0 deletions test/bitcoin.core.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var networks = require('../src/networks')
var Address = require('../src/address')
var BigInteger = require('bigi')
var ECKey = require('../src/eckey')
var ECSignature = require('../src/ecsignature')
var Transaction = require('../src/transaction')
var Script = require('../src/script')

Expand Down Expand Up @@ -197,4 +198,32 @@ describe('Bitcoin-core', function() {
})
})
})

describe('ECSignature', function() {
sig_canonical.forEach(function(hex) {
var buffer = new Buffer(hex, 'hex')

it('can parse ' + hex, function() {
var parsed = ECSignature.parseScriptSignature(buffer)
var actual = parsed.signature.toScriptSignature(parsed.hashType)
assert.equal(actual.toString('hex'), hex)
})
})

sig_noncanonical.forEach(function(hex, i) {
if (i === 0) return
if (i % 2 !== 0) return

var description = sig_noncanonical[i - 1].slice(0, -1)
if (description === 'too long') return // we support non secp256k1 signatures
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this suitable? Or should we just enforce secp256k1 as we have in other parts. If so, the signature length is at most going to be less than 73 bytes if so, as enforced by the reference implementation.

Copy link
Contributor

Choose a reason for hiding this comment

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

I support enforcing secp256k1 until we have a need for supporting other curves

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That won't help us if we ever use another ecdsa (with its own ECSignature) library other than the one currently included.

Copy link
Contributor

Choose a reason for hiding this comment

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

We can worry about it then?


var buffer = new Buffer(hex, 'hex')

it('throws on ' + description, function() {
assert.throws(function() {
ECSignature.parseScriptSignature(buffer)
})
})
})
})
})
38 changes: 31 additions & 7 deletions test/fixtures/ecsignature.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@
"i": 1
},
"scriptSignature": {
"hex": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cefff",
"hashType": 255
"hex": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef81",
"hashType": 129
},
"DER": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef",
"signature": {
Expand All @@ -131,23 +131,47 @@
"DER": [
{
"exception": "Invalid sequence length",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These test data amendments were because the R, S values were negative.

"hex": "30ff0204ffffffff0204ffffffff"
"hex": "30ff020400ffffff020400ffffff"
},
{
"exception": "Invalid sequence length",
"hex": "300c0304ffffffff0304ffffffff0000"
"hex": "300c030400ffffff030400ffffff0000"
},
{
"exception": "Expected a DER integer",
"hex": "300cff04ffffffff0204ffffffff"
"hex": "300cff0400ffffff020400ffffff"
},
{
"exception": "Expected a DER integer \\(2\\)",
"hex": "300c0202ffffffff0204ffffffff"
"hex": "300c020200ffffff020400ffffff"
},
{
"exception": "Invalid DER encoding",
"hex": "300c0204ffffffff0202ffffffff"
"hex": "300c020400ffffff020200ffffff"
},
{
"exception": "R length is zero",
"hex": "30080200020400ffffff"
},
{
"exception": "S length is zero",
"hex": "3008020400ffffff0200"
},
{
"exception": "R value is negative",
"hex": "300c0204ffffffff020400ffffff"
},
{
"exception": "S value is negative",
"hex": "300c020400ffffff0204ffffffff"
},
{
"exception": "R value excessively padded",
"hex": "300c02040000ffff020400ffffff"
},
{
"exception": "S value excessively padded",
"hex": "300c020400ffffff02040000ffff"
}
]
}
Expand Down