Daniel Cousens
11 years ago
9 changed files with 350 additions and 297 deletions
@ -0,0 +1,102 @@ |
|||
var assert = require('assert') |
|||
|
|||
var BigInteger = require('bigi') |
|||
|
|||
function ECSignature(r, s) { |
|||
assert(r instanceof BigInteger, 'Expected BigInteger, got ' + r) |
|||
assert(s instanceof BigInteger, 'Expected BigInteger, got ' + s) |
|||
this.r = r |
|||
this.s = s |
|||
} |
|||
|
|||
// Import operations
|
|||
ECSignature.fromCompact = function(buffer) { |
|||
assert.equal(buffer.length, 65, 'Invalid signature length') |
|||
var i = buffer.readUInt8(0) - 27 |
|||
|
|||
// At most 3 bits
|
|||
assert.equal(i, i & 7, 'Invalid signature parameter') |
|||
var compressed = !!(i & 4) |
|||
|
|||
// Recovery param only
|
|||
i = i & 3 |
|||
|
|||
var r = BigInteger.fromBuffer(buffer.slice(1, 33)) |
|||
var s = BigInteger.fromBuffer(buffer.slice(33)) |
|||
|
|||
return { |
|||
compressed: compressed, |
|||
i: i, |
|||
signature: new ECSignature(r, s) |
|||
} |
|||
} |
|||
|
|||
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) |
|||
|
|||
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) |
|||
offset += 2 + sLen |
|||
|
|||
assert.equal(offset, buffer.length, 'Invalid DER encoding') |
|||
var r = BigInteger.fromDERInteger(rB) |
|||
var s = BigInteger.fromDERInteger(sB) |
|||
|
|||
return new ECSignature(r, s) |
|||
} |
|||
|
|||
ECSignature.fromScriptSignature = function(buffer) { |
|||
return { |
|||
signature: ECSignature.fromDER(buffer.slice(0, -1)), |
|||
hashType: buffer.readUInt8(buffer.length - 1) |
|||
} |
|||
} |
|||
|
|||
// Export operations
|
|||
ECSignature.prototype.toCompact = function(i, compressed) { |
|||
if (compressed) i += 4 |
|||
i += 27 |
|||
|
|||
var buffer = new Buffer(65) |
|||
buffer.writeUInt8(i, 0) |
|||
|
|||
this.r.toBuffer(32).copy(buffer, 1) |
|||
this.s.toBuffer(32).copy(buffer, 33) |
|||
|
|||
return buffer |
|||
} |
|||
|
|||
ECSignature.prototype.toDER = function() { |
|||
var rBa = this.r.toDERInteger() |
|||
var sBa = this.s.toDERInteger() |
|||
|
|||
var sequence = [] |
|||
sequence.push(0x02) // INTEGER
|
|||
sequence.push(rBa.length) |
|||
sequence = sequence.concat(rBa) |
|||
|
|||
sequence.push(0x02) // INTEGER
|
|||
sequence.push(sBa.length) |
|||
sequence = sequence.concat(sBa) |
|||
|
|||
sequence.unshift(sequence.length) |
|||
sequence.unshift(0x30) // SEQUENCE
|
|||
|
|||
return new Buffer(sequence) |
|||
} |
|||
|
|||
ECSignature.prototype.toScriptSignature = function(hashType) { |
|||
var hashTypeBuffer = new Buffer(1) |
|||
hashTypeBuffer.writeUInt8(hashType, 0) |
|||
|
|||
return Buffer.concat([this.toDER(), hashTypeBuffer]) |
|||
} |
|||
|
|||
module.exports = ECSignature |
@ -0,0 +1,82 @@ |
|||
var assert = require('assert') |
|||
|
|||
var BigInteger = require('bigi') |
|||
var ECSignature = require('../src/ecsignature') |
|||
|
|||
var fixtures = require('./fixtures/ecsignature.json') |
|||
|
|||
describe('ECSignature', function() { |
|||
describe('toCompact', function() { |
|||
fixtures.valid.forEach(function(f) { |
|||
it('encodes ' + f.compact.hex + ' correctly', function() { |
|||
var signature = new ECSignature( |
|||
new BigInteger(f.signature.r), |
|||
new BigInteger(f.signature.s) |
|||
) |
|||
|
|||
var buffer = signature.toCompact(f.compact.i, f.compact.compressed) |
|||
assert.equal(buffer.toString('hex'), f.compact.hex) |
|||
}) |
|||
}) |
|||
}) |
|||
|
|||
describe('fromCompact', function() { |
|||
fixtures.valid.forEach(function(f) { |
|||
it('decodes ' + f.compact.hex + ' correctly', function() { |
|||
var buffer = new Buffer(f.compact.hex, 'hex') |
|||
var parsed = ECSignature.fromCompact(buffer) |
|||
|
|||
assert.equal(parsed.compressed, f.compact.compressed) |
|||
assert.equal(parsed.i, f.compact.i) |
|||
assert.equal(parsed.signature.r.toString(), f.signature.r) |
|||
assert.equal(parsed.signature.s.toString(), f.signature.s) |
|||
}) |
|||
}) |
|||
|
|||
fixtures.invalid.compact.forEach(function(f) { |
|||
it('throws on ' + f.hex, function() { |
|||
var buffer = new Buffer(f.hex, 'hex') |
|||
|
|||
assert.throws(function() { |
|||
ECSignature.fromCompact(buffer) |
|||
}, new RegExp(f.exception)) |
|||
}) |
|||
}) |
|||
}) |
|||
|
|||
describe('toDER', function() { |
|||
it('encodes a DER signature', function() { |
|||
fixtures.valid.forEach(function(f) { |
|||
var signature = new ECSignature( |
|||
new BigInteger(f.signature.r), |
|||
new BigInteger(f.signature.s) |
|||
) |
|||
|
|||
var DER = signature.toDER() |
|||
assert.equal(DER.toString('hex'), f.DER) |
|||
}) |
|||
}) |
|||
}) |
|||
|
|||
describe('fromDER', function() { |
|||
it('decodes the correct signature', function() { |
|||
fixtures.valid.forEach(function(f) { |
|||
var buffer = new Buffer(f.DER, 'hex') |
|||
var signature = ECSignature.fromDER(buffer) |
|||
|
|||
assert.equal(signature.r.toString(), f.signature.r) |
|||
assert.equal(signature.s.toString(), f.signature.s) |
|||
}) |
|||
}) |
|||
|
|||
fixtures.invalid.DER.forEach(function(f) { |
|||
it('throws on ' + f.hex, function() { |
|||
var buffer = new Buffer(f.hex, 'hex') |
|||
|
|||
assert.throws(function() { |
|||
ECSignature.fromDER(buffer) |
|||
}, new RegExp(f.exception)) |
|||
}) |
|||
}) |
|||
}) |
|||
}) |
@ -0,0 +1,126 @@ |
|||
{ |
|||
"valid": [ |
|||
{ |
|||
"compact": { |
|||
"hex": "1f33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262", |
|||
"compressed": true, |
|||
"i": 0 |
|||
}, |
|||
"DER": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262", |
|||
"signature": { |
|||
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", |
|||
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" |
|||
} |
|||
}, |
|||
{ |
|||
"compact": { |
|||
"hex": "1b54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5", |
|||
"compressed": false, |
|||
"i": 0 |
|||
}, |
|||
"DER": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5", |
|||
"signature": { |
|||
"r": "38341707918488238920692284707283974715538935465589664377561695343399725051885", |
|||
"s": "3180566392414476763164587487324397066658063772201694230600609996154610926757" |
|||
} |
|||
}, |
|||
{ |
|||
"compact": { |
|||
"hex": "1fff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283", |
|||
"compressed": true, |
|||
"i": 0 |
|||
}, |
|||
"DER": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283", |
|||
"signature": { |
|||
"r": "115464191557905790016094131873849783294273568009648050793030031933291767741904", |
|||
"s": "50562520307781850052192542766631199590053690478900449960232079510155113443971" |
|||
} |
|||
}, |
|||
{ |
|||
"compact": { |
|||
"hex": "1cc0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3", |
|||
"compressed": false, |
|||
"i": 1 |
|||
}, |
|||
"DER": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3", |
|||
"signature": { |
|||
"r": "87230998027579607140680851455601772643840468630989315269459846730712163783123", |
|||
"s": "53231320085894623106179381504478252331065330583563809963303318469380290929875" |
|||
} |
|||
}, |
|||
{ |
|||
"compact": { |
|||
"hex": "1f7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6", |
|||
"compressed": true, |
|||
"i": 0 |
|||
}, |
|||
"DER": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6", |
|||
"signature": { |
|||
"r": "51348483531757779992459563033975330355971795607481991320287437101831125115997", |
|||
"s": "6277080015686056199074771961940657638578000617958603212944619747099038735862" |
|||
} |
|||
}, |
|||
{ |
|||
"compact": { |
|||
"hex": "1cfbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37", |
|||
"compressed": false, |
|||
"i": 1 |
|||
}, |
|||
"DER": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37", |
|||
"signature": { |
|||
"r": "113979859486826658566290715281614250298918272782414232881639314569529560769671", |
|||
"s": "6517071009538626957379450615706485096874328019806177698938278220732027419959" |
|||
} |
|||
}, |
|||
{ |
|||
"compact": { |
|||
"hex": "20cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef", |
|||
"compressed": true, |
|||
"i": 1 |
|||
}, |
|||
"DER": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef", |
|||
"signature": { |
|||
"r": "93122007060065279508564838030979550535085999589142852106617159184757394422777", |
|||
"s": "3078539468410661027472930027406594684630312677495124015420811882501887769839" |
|||
} |
|||
} |
|||
], |
|||
"invalid": { |
|||
"compact": [ |
|||
{ |
|||
"exception": "Invalid signature parameter", |
|||
"hex": "23987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62" |
|||
}, |
|||
{ |
|||
"exception": "Invalid signature length", |
|||
"hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62000000" |
|||
}, |
|||
{ |
|||
"exception": "Invalid signature length", |
|||
"hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e9379" |
|||
} |
|||
], |
|||
"DER": [ |
|||
{ |
|||
"exception": "Invalid sequence length", |
|||
"hex": "30ff0204ffffffff0204ffffffff" |
|||
}, |
|||
{ |
|||
"exception": "Invalid sequence length", |
|||
"hex": "300c0304ffffffff0304ffffffff0000" |
|||
}, |
|||
{ |
|||
"exception": "Expected a DER integer", |
|||
"hex": "300cff04ffffffff0204ffffffff" |
|||
}, |
|||
{ |
|||
"exception": "Expected a DER integer \\(2\\)", |
|||
"hex": "300c0202ffffffff0204ffffffff" |
|||
}, |
|||
{ |
|||
"exception": "Invalid DER encoding", |
|||
"hex": "300c0204ffffffff0202ffffffff" |
|||
} |
|||
] |
|||
} |
|||
} |
Loading…
Reference in new issue