Skip to content

Commit

Permalink
Merge pull request #1084 from bitcoinjs/ss64
Browse files Browse the repository at this point in the history
ECPair/script_signature: switch to 64-byte RS buffers only
  • Loading branch information
dcousens authored May 22, 2018
2 parents 0d76f00 + a463c41 commit 58b465f
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 70 deletions.
8 changes: 7 additions & 1 deletion src/ecpair.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand All @@ -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)
}

Expand Down
70 changes: 37 additions & 33 deletions src/script_signature.js
Original file line number Diff line number Diff line change
@@ -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),
Expand All @@ -53,8 +59,6 @@ function encode (signature, hashType) {
}

module.exports = {
fromRSBuffer: fromRSBuffer,
toRSBuffer: toRSBuffer,
decode: decode,
encode: encode
}
4 changes: 1 addition & 3 deletions src/transaction_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
Expand Down
7 changes: 6 additions & 1 deletion test/ecpair.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)

Expand Down
36 changes: 18 additions & 18 deletions test/fixtures/signature.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
],
Expand Down Expand Up @@ -123,17 +123,17 @@
"hashType": 7,
"hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226207",
"raw": {
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561",
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938"
"r": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9",
"s": "6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"
}
},
{
"exception": "Invalid hashType 140",
"hashType": 140,
"hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa543422628c",
"raw": {
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561",
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938"
"r": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9",
"s": "6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"
}
}
]
Expand Down
12 changes: 7 additions & 5 deletions test/integration/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
13 changes: 6 additions & 7 deletions test/script_signature.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
}
}

Expand Down
4 changes: 2 additions & 2 deletions test/transaction_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,15 +298,15 @@ describe('TransactionBuilder', function () {
it('supports the alternative abstract interface { publicKey, sign }', function () {
var keyPair = {
publicKey: Buffer.alloc(33, 0x03),
sign: function (hash) { return Buffer.alloc(64) }
sign: function (hash) { return Buffer.alloc(64, 0x5f) }
}

var txb = new TransactionBuilder()
txb.setVersion(1)
txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1)
txb.addOutput('1111111111111111111114oLvT2', 100000)
txb.sign(0, keyPair)
assert.equal(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000002c0930060201000201000121030303030303030303030303030303030303030303030303030303030303030303ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000')
assert.equal(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121030303030303030303030303030303030303030303030303030303030303030303ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000')
})

fixtures.invalid.sign.forEach(function (f) {
Expand Down

0 comments on commit 58b465f

Please sign in to comment.