ECPair/script_signature: switch to 64-byte RS buffers only

This commit is contained in:
Daniel Cousens 2018-05-15 10:07:11 +10:00
parent 0d76f00917
commit 6c4977983d
7 changed files with 82 additions and 68 deletions

View file

@ -115,7 +115,8 @@ ECPair.prototype.getPublicKeyBuffer = function () {
ECPair.prototype.sign = function (hash) { ECPair.prototype.sign = function (hash) {
if (!this.d) throw new Error('Missing private key') if (!this.d) throw new Error('Missing private key')
return ecdsa.sign(hash, this.d) let signature = ecdsa.sign(hash, this.d)
return Buffer.concat([signature.r.toBuffer(32), signature.s.toBuffer(32)], 64)
} }
ECPair.prototype.toWIF = function () { ECPair.prototype.toWIF = function () {
@ -125,6 +126,11 @@ ECPair.prototype.toWIF = function () {
} }
ECPair.prototype.verify = function (hash, signature) { ECPair.prototype.verify = function (hash, signature) {
signature = {
r: BigInteger.fromBuffer(signature.slice(0, 32)),
s: BigInteger.fromBuffer(signature.slice(32, 64))
}
return ecdsa.verify(hash, signature, this.Q) return ecdsa.verify(hash, signature, this.Q)
} }

View file

@ -1,50 +1,56 @@
var bip66 = require('bip66') let bip66 = require('bip66')
var BigInteger = require('bigi') let Buffer = require('safe-buffer').Buffer
var Buffer = require('safe-buffer').Buffer let typeforce = require('typeforce')
var typeforce = require('typeforce') let types = require('./types')
var types = require('./types')
let ZERO = Buffer.alloc(1, 0)
function toDER (x) {
let i = 0
while (x[i] === 0) ++i
if (i === x.length) return ZERO
x = x.slice(i)
if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length)
return x
}
function fromDER (x) {
if (x[0] === 0x00) x = x.slice(1)
let buffer = Buffer.alloc(32, 0)
let bstart = Math.max(0, 32 - x.length)
x.copy(buffer, bstart)
return buffer
}
// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed) // BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
function decode (buffer) { function decode (buffer) {
var hashType = buffer.readUInt8(buffer.length - 1) let hashType = buffer.readUInt8(buffer.length - 1)
var hashTypeMod = hashType & ~0x80 let hashTypeMod = hashType & ~0x80
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType)
var decode = bip66.decode(buffer.slice(0, -1)) let decode = bip66.decode(buffer.slice(0, -1))
let r = fromDER(decode.r)
let s = fromDER(decode.s)
return { return {
signature: { signature: Buffer.concat([r, s], 64),
r: BigInteger.fromDERInteger(decode.r),
s: BigInteger.fromDERInteger(decode.s)
},
hashType: hashType hashType: hashType
} }
} }
function toRSBuffer (signature, buffer, offset) {
buffer = buffer || Buffer.alloc(64)
signature.r.toBuffer(32).copy(buffer, offset)
signature.s.toBuffer(32).copy(buffer, offset + 32)
return buffer
}
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) { function encode (signature, hashType) {
var hashTypeMod = hashType & ~0x80 typeforce({
signature: types.BufferN(64),
hashType: types.UInt8
}, { signature, hashType })
let hashTypeMod = hashType & ~0x80
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType)
var hashTypeBuffer = Buffer.allocUnsafe(1) let hashTypeBuffer = Buffer.allocUnsafe(1)
hashTypeBuffer.writeUInt8(hashType, 0) hashTypeBuffer.writeUInt8(hashType, 0)
var r = Buffer.from(signature.r.toDERInteger()) let r = toDER(signature.slice(0, 32))
var s = Buffer.from(signature.s.toDERInteger()) let s = toDER(signature.slice(32, 64))
return Buffer.concat([ return Buffer.concat([
bip66.encode(r, s), bip66.encode(r, s),
@ -53,8 +59,6 @@ function encode (signature, hashType) {
} }
module.exports = { module.exports = {
fromRSBuffer: fromRSBuffer,
toRSBuffer: toRSBuffer,
decode: decode, decode: decode,
encode: encode encode: encode
} }

View file

@ -715,9 +715,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy
input.prevOutType === scriptTypes.P2WSH input.prevOutType === scriptTypes.P2WSH
)) throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH') )) throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH')
var signature = keyPair.sign(signatureHash) let signature = keyPair.sign(signatureHash)
if (Buffer.isBuffer(signature)) signature = bscript.signature.fromRSBuffer(signature)
input.signatures[i] = bscript.signature.encode(signature, hashType) input.signatures[i] = bscript.signature.encode(signature, hashType)
return true return true
}) })

View file

@ -230,6 +230,7 @@ describe('ECPair', function () {
this.mock(ecdsa, 'sign', function (h, d) { this.mock(ecdsa, 'sign', function (h, d) {
assert.strictEqual(h, hash) assert.strictEqual(h, hash)
assert.strictEqual(d, keyPair.d) assert.strictEqual(d, keyPair.d)
return { r: BigInteger.ONE, s: BigInteger.ONE }
}, 1) }, 1)
keyPair.sign(hash) keyPair.sign(hash)
@ -254,7 +255,11 @@ describe('ECPair', function () {
it('wraps ecdsa.verify', hoodwink(function () { it('wraps ecdsa.verify', hoodwink(function () {
this.mock(ecdsa, 'verify', function (h, s, q) { this.mock(ecdsa, 'verify', function (h, s, q) {
assert.strictEqual(h, hash) assert.strictEqual(h, hash)
assert.strictEqual(s, signature) // assert.strictEqual(s, signature)
assert.deepEqual(s, {
r: BigInteger.fromBuffer(signature.slice(0, 32)),
s: BigInteger.fromBuffer(signature.slice(32, 64))
})
assert.strictEqual(q, keyPair.Q) assert.strictEqual(q, keyPair.Q)
}, 1) }, 1)

View file

@ -4,56 +4,56 @@
"hashType": 1, "hashType": 1,
"hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226201", "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226201",
"raw": { "raw": {
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", "r": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9",
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" "s": "6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"
} }
}, },
{ {
"hashType": 2, "hashType": 2,
"hex": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a502", "hex": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a502",
"raw": { "raw": {
"r": "38341707918488238920692284707283974715538935465589664377561695343399725051885", "r": "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed",
"s": "3180566392414476763164587487324397066658063772201694230600609996154610926757" "s": "07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"
} }
}, },
{ {
"hashType": 3, "hashType": 3,
"hex": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b28303", "hex": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b28303",
"raw": { "raw": {
"r": "115464191557905790016094131873849783294273568009648050793030031933291767741904", "r": "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd0",
"s": "50562520307781850052192542766631199590053690478900449960232079510155113443971" "s": "6fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"
} }
}, },
{ {
"hashType": 129, "hashType": 129,
"hex": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d381", "hex": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d381",
"raw": { "raw": {
"r": "87230998027579607140680851455601772643840468630989315269459846730712163783123", "r": "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3",
"s": "53231320085894623106179381504478252331065330583563809963303318469380290929875" "s": "75afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3"
} }
}, },
{ {
"hashType": 130, "hashType": 130,
"hex": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df682", "hex": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df682",
"raw": { "raw": {
"r": "51348483531757779992459563033975330355971795607481991320287437101831125115997", "r": "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d",
"s": "6277080015686056199074771961940657638578000617958603212944619747099038735862" "s": "0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6"
} }
}, },
{ {
"hashType": 131, "hashType": 131,
"hex": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd3783", "hex": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd3783",
"raw": { "raw": {
"r": "113979859486826658566290715281614250298918272782414232881639314569529560769671", "r": "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda487",
"s": "6517071009538626957379450615706485096874328019806177698938278220732027419959" "s": "0e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37"
} }
}, },
{ {
"hashType": 129, "hashType": 129,
"hex": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef81", "hex": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef81",
"raw": { "raw": {
"r": "93122007060065279508564838030979550535085999589142852106617159184757394422777", "r": "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9",
"s": "3078539468410661027472930027406594684630312677495124015420811882501887769839" "s": "06ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef"
} }
} }
], ],
@ -123,8 +123,8 @@
"hashType": 7, "hashType": 7,
"hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226207", "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226207",
"raw": { "raw": {
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", "r": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9",
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" "s": "6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"
} }
}, },
{ {
@ -132,8 +132,8 @@
"hashType": 140, "hashType": 140,
"hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa543422628c", "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa543422628c",
"raw": { "raw": {
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", "r": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9",
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" "s": "6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"
} }
} }
] ]

View file

@ -43,12 +43,14 @@ describe('bitcoinjs-lib (crypto)', function () {
var inputB = tx.ins[j] var inputB = tx.ins[j]
// enforce matching r values // enforce matching r values
assert.strictEqual(inputA.signature.r.toString(), inputB.signature.r.toString()) let r = inputA.signature.slice(0, 32)
var r = inputA.signature.r let rB = inputB.signature.slice(0, 32)
var rInv = r.modInverse(n) assert.strictEqual(r.toString('hex'), rB.toString('hex'))
var s1 = inputA.signature.s var rInv = bigi.fromBuffer(r).modInverse(n)
var s2 = inputB.signature.s
var s1 = bigi.fromBuffer(inputA.signature.slice(32, 64))
var s2 = bigi.fromBuffer(inputB.signature.slice(32, 64))
var z1 = inputA.z var z1 = inputA.z
var z2 = inputB.z var z2 = inputB.z

View file

@ -2,22 +2,21 @@
var assert = require('assert') var assert = require('assert')
var bscriptSig = require('../src/script').signature var bscriptSig = require('../src/script').signature
var BigInteger = require('bigi')
var Buffer = require('safe-buffer').Buffer var Buffer = require('safe-buffer').Buffer
var fixtures = require('./fixtures/signature.json') var fixtures = require('./fixtures/signature.json')
describe('Script Signatures', function () { describe('Script Signatures', function () {
function fromRaw (signature) { function fromRaw (signature) {
return { return Buffer.concat([
r: new BigInteger(signature.r), Buffer.from(signature.r, 'hex'),
s: new BigInteger(signature.s) Buffer.from(signature.s, 'hex')
} ], 64)
} }
function toRaw (signature) { function toRaw (signature) {
return { return {
r: signature.r.toString(), r: signature.slice(0, 32).toString('hex'),
s: signature.s.toString() s: signature.slice(32, 64).toString('hex')
} }
} }