14 changed files with 335 additions and 149 deletions
@ -1,97 +0,0 @@ |
|||||
var bip66 = require('bip66') |
|
||||
var typeforce = require('typeforce') |
|
||||
var types = require('./types') |
|
||||
|
|
||||
var BigInteger = require('bigi') |
|
||||
|
|
||||
function ECSignature (r, s) { |
|
||||
typeforce(types.tuple(types.BigInt, types.BigInt), arguments) |
|
||||
|
|
||||
this.r = r |
|
||||
this.s = s |
|
||||
} |
|
||||
|
|
||||
ECSignature.parseCompact = function (buffer) { |
|
||||
typeforce(types.BufferN(65), buffer) |
|
||||
|
|
||||
var flagByte = buffer.readUInt8(0) - 27 |
|
||||
if (flagByte !== (flagByte & 7)) throw new Error('Invalid signature parameter') |
|
||||
|
|
||||
var compressed = !!(flagByte & 4) |
|
||||
var recoveryParam = flagByte & 3 |
|
||||
var signature = ECSignature.fromRSBuffer(buffer.slice(1)) |
|
||||
|
|
||||
return { |
|
||||
compressed: compressed, |
|
||||
i: recoveryParam, |
|
||||
signature: signature |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
ECSignature.fromRSBuffer = function (buffer) { |
|
||||
typeforce(types.BufferN(64), buffer) |
|
||||
|
|
||||
var r = BigInteger.fromBuffer(buffer.slice(0, 32)) |
|
||||
var s = BigInteger.fromBuffer(buffer.slice(32, 64)) |
|
||||
return new ECSignature(r, s) |
|
||||
} |
|
||||
|
|
||||
ECSignature.fromDER = function (buffer) { |
|
||||
var decode = bip66.decode(buffer) |
|
||||
var r = BigInteger.fromDERInteger(decode.r) |
|
||||
var s = BigInteger.fromDERInteger(decode.s) |
|
||||
|
|
||||
return new ECSignature(r, s) |
|
||||
} |
|
||||
|
|
||||
// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
|
|
||||
ECSignature.parseScriptSignature = function (buffer) { |
|
||||
var hashType = buffer.readUInt8(buffer.length - 1) |
|
||||
var hashTypeMod = hashType & ~0x80 |
|
||||
|
|
||||
if (hashTypeMod <= 0x00 || hashTypeMod >= 0x04) throw new Error('Invalid hashType ' + hashType) |
|
||||
|
|
||||
return { |
|
||||
signature: ECSignature.fromDER(buffer.slice(0, -1)), |
|
||||
hashType: hashType |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
ECSignature.prototype.toCompact = function (i, compressed) { |
|
||||
if (compressed) { |
|
||||
i += 4 |
|
||||
} |
|
||||
|
|
||||
i += 27 |
|
||||
|
|
||||
var buffer = Buffer.alloc(65) |
|
||||
buffer.writeUInt8(i, 0) |
|
||||
this.toRSBuffer(buffer, 1) |
|
||||
return buffer |
|
||||
} |
|
||||
|
|
||||
ECSignature.prototype.toDER = function () { |
|
||||
var r = Buffer.from(this.r.toDERInteger()) |
|
||||
var s = Buffer.from(this.s.toDERInteger()) |
|
||||
|
|
||||
return bip66.encode(r, s) |
|
||||
} |
|
||||
|
|
||||
ECSignature.prototype.toRSBuffer = function (buffer, offset) { |
|
||||
buffer = buffer || Buffer.alloc(64) |
|
||||
this.r.toBuffer(32).copy(buffer, offset) |
|
||||
this.s.toBuffer(32).copy(buffer, offset + 32) |
|
||||
return buffer |
|
||||
} |
|
||||
|
|
||||
ECSignature.prototype.toScriptSignature = function (hashType) { |
|
||||
var hashTypeMod = hashType & ~0x80 |
|
||||
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) |
|
||||
|
|
||||
var hashTypeBuffer = Buffer.alloc(1) |
|
||||
hashTypeBuffer.writeUInt8(hashType, 0) |
|
||||
|
|
||||
return Buffer.concat([this.toDER(), hashTypeBuffer]) |
|
||||
} |
|
||||
|
|
||||
module.exports = ECSignature |
|
@ -0,0 +1,51 @@ |
|||||
|
var bip66 = require('bip66') |
||||
|
var BigInteger = require('bigi') |
||||
|
var typeforce = require('typeforce') |
||||
|
var types = require('./types') |
||||
|
|
||||
|
// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
|
||||
|
function decode (buffer) { |
||||
|
var hashType = buffer.readUInt8(buffer.length - 1) |
||||
|
var hashTypeMod = hashType & ~0x80 |
||||
|
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) |
||||
|
|
||||
|
var decode = bip66.decode(buffer.slice(0, -1)) |
||||
|
|
||||
|
return { |
||||
|
signature: { |
||||
|
r: BigInteger.fromDERInteger(decode.r), |
||||
|
s: BigInteger.fromDERInteger(decode.s) |
||||
|
}, |
||||
|
hashType: hashType |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function fromRSBuffer (buffer) { |
||||
|
typeforce(types.BufferN(64), buffer) |
||||
|
|
||||
|
var r = BigInteger.fromBuffer(buffer.slice(0, 32)) |
||||
|
var s = BigInteger.fromBuffer(buffer.slice(32, 64)) |
||||
|
return { r: r, s: s } |
||||
|
} |
||||
|
|
||||
|
function encode (signature, hashType) { |
||||
|
var hashTypeMod = hashType & ~0x80 |
||||
|
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) |
||||
|
|
||||
|
var hashTypeBuffer = new Buffer(1) |
||||
|
hashTypeBuffer.writeUInt8(hashType, 0) |
||||
|
|
||||
|
var r = new Buffer(signature.r.toDERInteger()) |
||||
|
var s = new Buffer(signature.s.toDERInteger()) |
||||
|
|
||||
|
return Buffer.concat([ |
||||
|
bip66.encode(r, s), |
||||
|
hashTypeBuffer |
||||
|
]) |
||||
|
} |
||||
|
|
||||
|
module.exports = { |
||||
|
fromRSBuffer, |
||||
|
decode: decode, |
||||
|
encode: encode |
||||
|
} |
@ -0,0 +1,140 @@ |
|||||
|
{ |
||||
|
"valid": [ |
||||
|
{ |
||||
|
"hashType": 1, |
||||
|
"hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226201", |
||||
|
"raw": { |
||||
|
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", |
||||
|
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"hashType": 2, |
||||
|
"hex": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a502", |
||||
|
"raw": { |
||||
|
"r": "38341707918488238920692284707283974715538935465589664377561695343399725051885", |
||||
|
"s": "3180566392414476763164587487324397066658063772201694230600609996154610926757" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"hashType": 3, |
||||
|
"hex": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b28303", |
||||
|
"raw": { |
||||
|
"r": "115464191557905790016094131873849783294273568009648050793030031933291767741904", |
||||
|
"s": "50562520307781850052192542766631199590053690478900449960232079510155113443971" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"hashType": 129, |
||||
|
"hex": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d381", |
||||
|
"raw": { |
||||
|
"r": "87230998027579607140680851455601772643840468630989315269459846730712163783123", |
||||
|
"s": "53231320085894623106179381504478252331065330583563809963303318469380290929875" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"hashType": 130, |
||||
|
"hex": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df682", |
||||
|
"raw": { |
||||
|
"r": "51348483531757779992459563033975330355971795607481991320287437101831125115997", |
||||
|
"s": "6277080015686056199074771961940657638578000617958603212944619747099038735862" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"hashType": 131, |
||||
|
"hex": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd3783", |
||||
|
"raw": { |
||||
|
"r": "113979859486826658566290715281614250298918272782414232881639314569529560769671", |
||||
|
"s": "6517071009538626957379450615706485096874328019806177698938278220732027419959" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"hashType": 129, |
||||
|
"hex": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef81", |
||||
|
"raw": { |
||||
|
"r": "93122007060065279508564838030979550535085999589142852106617159184757394422777", |
||||
|
"s": "3078539468410661027472930027406594684630312677495124015420811882501887769839" |
||||
|
} |
||||
|
} |
||||
|
], |
||||
|
"invalid": [ |
||||
|
{ |
||||
|
"exception": "DER sequence length is too short", |
||||
|
"hex": "ffffffffffffff01" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "DER sequence length is too long", |
||||
|
"hex": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "Expected DER sequence", |
||||
|
"hex": "00ffff0400ffffff020400ffffff01" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "DER sequence length is invalid", |
||||
|
"hex": "30ff020400ffffff020400ffffff01" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "DER sequence length is invalid", |
||||
|
"hex": "300c030400ffffff030400ffffff000001" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "Expected DER integer", |
||||
|
"hex": "300cff0400ffffff020400ffffff01" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "Expected DER integer \\(2\\)", |
||||
|
"hex": "300c020200ffffff020400ffffff01" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "R length is zero", |
||||
|
"hex": "30080200020400ffffff01" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "S length is zero", |
||||
|
"hex": "3008020400ffffff020001" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "R length is too long", |
||||
|
"hex": "300c02dd00ffffff020400ffffff01" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "S length is invalid", |
||||
|
"hex": "300c020400ffffff02dd00ffffff01" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "R value is negative", |
||||
|
"hex": "300c020480000000020400ffffff01" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "S value is negative", |
||||
|
"hex": "300c020400ffffff02048000000001" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "R value excessively padded", |
||||
|
"hex": "300c02040000ffff020400ffffff01" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "S value excessively padded", |
||||
|
"hex": "300c020400ffffff02040000ffff01" |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "Invalid hashType 7", |
||||
|
"hashType": 7, |
||||
|
"hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226207", |
||||
|
"raw": { |
||||
|
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", |
||||
|
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"exception": "Invalid hashType 140", |
||||
|
"hashType": 140, |
||||
|
"hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa543422628c", |
||||
|
"raw": { |
||||
|
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", |
||||
|
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
@ -0,0 +1,65 @@ |
|||||
|
/* global describe, it */ |
||||
|
|
||||
|
var assert = require('assert') |
||||
|
var bscriptSig = require('../src/script').signature |
||||
|
var BigInteger = require('bigi') |
||||
|
var fixtures = require('./fixtures/signature.json') |
||||
|
|
||||
|
describe('Script Signatures', function () { |
||||
|
function fromRaw (signature) { |
||||
|
return { |
||||
|
r: new BigInteger(signature.r), |
||||
|
s: new BigInteger(signature.s) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function toRaw (signature) { |
||||
|
return { |
||||
|
r: signature.r.toString(), |
||||
|
s: signature.s.toString() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
describe('encode', function () { |
||||
|
fixtures.valid.forEach(function (f) { |
||||
|
it('encodes ' + f.hex, function () { |
||||
|
var buffer = bscriptSig.encode(fromRaw(f.raw), f.hashType) |
||||
|
|
||||
|
assert.strictEqual(buffer.toString('hex'), f.hex) |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
fixtures.invalid.forEach(function (f) { |
||||
|
if (!f.raw) return |
||||
|
|
||||
|
it('throws ' + f.exception, function () { |
||||
|
var signature = fromRaw(f.raw) |
||||
|
|
||||
|
assert.throws(function () { |
||||
|
bscriptSig.encode(signature, f.hashType) |
||||
|
}, new RegExp(f.exception)) |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
describe('decode', function () { |
||||
|
fixtures.valid.forEach(function (f) { |
||||
|
it('decodes ' + f.hex, function () { |
||||
|
var decode = bscriptSig.decode(new Buffer(f.hex, 'hex')) |
||||
|
|
||||
|
assert.deepEqual(toRaw(decode.signature), f.raw) |
||||
|
assert.strictEqual(decode.hashType, f.hashType) |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
fixtures.invalid.forEach(function (f) { |
||||
|
it('throws on ' + f.hex, function () { |
||||
|
var buffer = new Buffer(f.hex, 'hex') |
||||
|
|
||||
|
assert.throws(function () { |
||||
|
bscriptSig.decode(buffer) |
||||
|
}, new RegExp(f.exception)) |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
|
}) |
Loading…
Reference in new issue