ECPair/script_signature: switch to 64-byte RS buffers only
This commit is contained in:
parent
0d76f00917
commit
6c4977983d
7 changed files with 82 additions and 68 deletions
|
@ -115,7 +115,8 @@ ECPair.prototype.getPublicKeyBuffer = function () {
|
|||
ECPair.prototype.sign = function (hash) {
|
||||
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 () {
|
||||
|
@ -125,6 +126,11 @@ ECPair.prototype.toWIF = function () {
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,50 +1,56 @@
|
|||
var bip66 = require('bip66')
|
||||
var BigInteger = require('bigi')
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var typeforce = require('typeforce')
|
||||
var types = require('./types')
|
||||
let bip66 = require('bip66')
|
||||
let Buffer = require('safe-buffer').Buffer
|
||||
let typeforce = require('typeforce')
|
||||
let 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)
|
||||
function decode (buffer) {
|
||||
var hashType = buffer.readUInt8(buffer.length - 1)
|
||||
var hashTypeMod = hashType & ~0x80
|
||||
let hashType = buffer.readUInt8(buffer.length - 1)
|
||||
let hashTypeMod = hashType & ~0x80
|
||||
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 {
|
||||
signature: {
|
||||
r: BigInteger.fromDERInteger(decode.r),
|
||||
s: BigInteger.fromDERInteger(decode.s)
|
||||
},
|
||||
signature: Buffer.concat([r, s], 64),
|
||||
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) {
|
||||
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)
|
||||
|
||||
var hashTypeBuffer = Buffer.allocUnsafe(1)
|
||||
let hashTypeBuffer = Buffer.allocUnsafe(1)
|
||||
hashTypeBuffer.writeUInt8(hashType, 0)
|
||||
|
||||
var r = Buffer.from(signature.r.toDERInteger())
|
||||
var s = Buffer.from(signature.s.toDERInteger())
|
||||
let r = toDER(signature.slice(0, 32))
|
||||
let s = toDER(signature.slice(32, 64))
|
||||
|
||||
return Buffer.concat([
|
||||
bip66.encode(r, s),
|
||||
|
@ -53,8 +59,6 @@ function encode (signature, hashType) {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
fromRSBuffer: fromRSBuffer,
|
||||
toRSBuffer: toRSBuffer,
|
||||
decode: decode,
|
||||
encode: encode
|
||||
}
|
||||
|
|
|
@ -715,9 +715,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy
|
|||
input.prevOutType === scriptTypes.P2WSH
|
||||
)) throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH')
|
||||
|
||||
var signature = keyPair.sign(signatureHash)
|
||||
if (Buffer.isBuffer(signature)) signature = bscript.signature.fromRSBuffer(signature)
|
||||
|
||||
let signature = keyPair.sign(signatureHash)
|
||||
input.signatures[i] = bscript.signature.encode(signature, hashType)
|
||||
return true
|
||||
})
|
||||
|
|
|
@ -230,6 +230,7 @@ describe('ECPair', function () {
|
|||
this.mock(ecdsa, 'sign', function (h, d) {
|
||||
assert.strictEqual(h, hash)
|
||||
assert.strictEqual(d, keyPair.d)
|
||||
return { r: BigInteger.ONE, s: BigInteger.ONE }
|
||||
}, 1)
|
||||
|
||||
keyPair.sign(hash)
|
||||
|
@ -254,7 +255,11 @@ describe('ECPair', function () {
|
|||
it('wraps ecdsa.verify', hoodwink(function () {
|
||||
this.mock(ecdsa, 'verify', function (h, s, q) {
|
||||
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)
|
||||
}, 1)
|
||||
|
||||
|
|
36
test/fixtures/signature.json
vendored
36
test/fixtures/signature.json
vendored
|
@ -4,56 +4,56 @@
|
|||
"hashType": 1,
|
||||
"hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226201",
|
||||
"raw": {
|
||||
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561",
|
||||
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938"
|
||||
"r": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9",
|
||||
"s": "6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hashType": 2,
|
||||
"hex": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a502",
|
||||
"raw": {
|
||||
"r": "38341707918488238920692284707283974715538935465589664377561695343399725051885",
|
||||
"s": "3180566392414476763164587487324397066658063772201694230600609996154610926757"
|
||||
"r": "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed",
|
||||
"s": "07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hashType": 3,
|
||||
"hex": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b28303",
|
||||
"raw": {
|
||||
"r": "115464191557905790016094131873849783294273568009648050793030031933291767741904",
|
||||
"s": "50562520307781850052192542766631199590053690478900449960232079510155113443971"
|
||||
"r": "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd0",
|
||||
"s": "6fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hashType": 129,
|
||||
"hex": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d381",
|
||||
"raw": {
|
||||
"r": "87230998027579607140680851455601772643840468630989315269459846730712163783123",
|
||||
"s": "53231320085894623106179381504478252331065330583563809963303318469380290929875"
|
||||
"r": "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3",
|
||||
"s": "75afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hashType": 130,
|
||||
"hex": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df682",
|
||||
"raw": {
|
||||
"r": "51348483531757779992459563033975330355971795607481991320287437101831125115997",
|
||||
"s": "6277080015686056199074771961940657638578000617958603212944619747099038735862"
|
||||
"r": "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d",
|
||||
"s": "0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hashType": 131,
|
||||
"hex": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd3783",
|
||||
"raw": {
|
||||
"r": "113979859486826658566290715281614250298918272782414232881639314569529560769671",
|
||||
"s": "6517071009538626957379450615706485096874328019806177698938278220732027419959"
|
||||
"r": "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda487",
|
||||
"s": "0e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hashType": 129,
|
||||
"hex": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef81",
|
||||
"raw": {
|
||||
"r": "93122007060065279508564838030979550535085999589142852106617159184757394422777",
|
||||
"s": "3078539468410661027472930027406594684630312677495124015420811882501887769839"
|
||||
"r": "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9",
|
||||
"s": "06ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -123,8 +123,8 @@
|
|||
"hashType": 7,
|
||||
"hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226207",
|
||||
"raw": {
|
||||
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561",
|
||||
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938"
|
||||
"r": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9",
|
||||
"s": "6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -132,8 +132,8 @@
|
|||
"hashType": 140,
|
||||
"hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa543422628c",
|
||||
"raw": {
|
||||
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561",
|
||||
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938"
|
||||
"r": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9",
|
||||
"s": "6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -43,12 +43,14 @@ describe('bitcoinjs-lib (crypto)', function () {
|
|||
var inputB = tx.ins[j]
|
||||
|
||||
// enforce matching r values
|
||||
assert.strictEqual(inputA.signature.r.toString(), inputB.signature.r.toString())
|
||||
var r = inputA.signature.r
|
||||
var rInv = r.modInverse(n)
|
||||
let r = inputA.signature.slice(0, 32)
|
||||
let rB = inputB.signature.slice(0, 32)
|
||||
assert.strictEqual(r.toString('hex'), rB.toString('hex'))
|
||||
|
||||
var s1 = inputA.signature.s
|
||||
var s2 = inputB.signature.s
|
||||
var rInv = bigi.fromBuffer(r).modInverse(n)
|
||||
|
||||
var s1 = bigi.fromBuffer(inputA.signature.slice(32, 64))
|
||||
var s2 = bigi.fromBuffer(inputB.signature.slice(32, 64))
|
||||
var z1 = inputA.z
|
||||
var z2 = inputB.z
|
||||
|
||||
|
|
|
@ -2,22 +2,21 @@
|
|||
|
||||
var assert = require('assert')
|
||||
var bscriptSig = require('../src/script').signature
|
||||
var BigInteger = require('bigi')
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var fixtures = require('./fixtures/signature.json')
|
||||
|
||||
describe('Script Signatures', function () {
|
||||
function fromRaw (signature) {
|
||||
return {
|
||||
r: new BigInteger(signature.r),
|
||||
s: new BigInteger(signature.s)
|
||||
}
|
||||
return Buffer.concat([
|
||||
Buffer.from(signature.r, 'hex'),
|
||||
Buffer.from(signature.s, 'hex')
|
||||
], 64)
|
||||
}
|
||||
|
||||
function toRaw (signature) {
|
||||
return {
|
||||
r: signature.r.toString(),
|
||||
s: signature.s.toString()
|
||||
r: signature.slice(0, 32).toString('hex'),
|
||||
s: signature.slice(32, 64).toString('hex')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue