|
|
|
var assert = require('assert')
|
|
|
|
var typeforce = require('typeforce')
|
|
|
|
var types = require('./types')
|
|
|
|
|
|
|
|
var BigInteger = require('bigi')
|
|
|
|
|
|
|
|
function ECSignature (r, s) {
|
|
|
|
typeforce(types.BigInt, r)
|
|
|
|
typeforce(types.BigInt, s)
|
|
|
|
|
|
|
|
this.r = r
|
|
|
|
this.s = s
|
|
|
|
}
|
|
|
|
|
|
|
|
ECSignature.parseCompact = 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Strict DER - https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
|
|
|
|
// NOTE: SIGHASH byte ignored
|
|
|
|
ECSignature.fromDER = function (buffer) {
|
|
|
|
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
|
|
|
|
if (buffer.length < 8) throw new Error('DER sequence too short')
|
|
|
|
if (buffer.length > 72) throw new Error('DER sequence too long')
|
|
|
|
if (buffer[0] !== 0x30) throw new Error('Not a DER sequence')
|
|
|
|
if (buffer[1] !== buffer.length - 2) throw new Error('Invalid sequence length')
|
|
|
|
if (buffer[2] !== 0x02) throw new Error('Expected a DER integer')
|
|
|
|
|
|
|
|
var lenR = buffer.readUInt8(3)
|
|
|
|
if (lenR === 0) throw new Error('R length is zero')
|
|
|
|
if (5 + lenR >= buffer.length) throw new Error('Invalid DER encoding')
|
|
|
|
if (buffer[4 + lenR] !== 0x02) throw new Error('Expected a DER integer (2)')
|
|
|
|
|
|
|
|
var lenS = buffer[5 + lenR]
|
|
|
|
if (lenS === 0) throw new Error('S length is zero')
|
|
|
|
if ((lenR + lenS + 6) !== buffer.length) throw new Error('Invalid DER encoding (2)')
|
|
|
|
|
|
|
|
if (buffer[4] & 0x80) throw new Error('R value is negative')
|
|
|
|
if (lenR > 1 && (buffer[4] === 0x00) && !(buffer[5] & 0x80)) throw new Error('R value excessively padded')
|
|
|
|
|
|
|
|
if (buffer[lenR + 6] & 0x80) throw new Error('S value is negative')
|
|
|
|
if (lenS > 1 && (buffer[lenR + 6] === 0x00) && !(buffer[lenR + 7] & 0x80)) throw new Error('S value excessively padded')
|
|
|
|
|
|
|
|
// non-BIP66 - extract R, S values
|
|
|
|
var rB = buffer.slice(4, 4 + lenR)
|
|
|
|
var sB = buffer.slice(lenR + 6)
|
|
|
|
var r = BigInteger.fromDERInteger(rB)
|
|
|
|
var s = BigInteger.fromDERInteger(sB)
|
|
|
|
|
|
|
|
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 = 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 = []
|
|
|
|
|
|
|
|
// INTEGER
|
|
|
|
sequence.push(0x02, rBa.length)
|
|
|
|
sequence = sequence.concat(rBa)
|
|
|
|
|
|
|
|
// INTEGER
|
|
|
|
sequence.push(0x02, sBa.length)
|
|
|
|
sequence = sequence.concat(sBa)
|
|
|
|
|
|
|
|
// SEQUENCE
|
|
|
|
sequence.unshift(0x30, sequence.length)
|
|
|
|
|
|
|
|
return new Buffer(sequence)
|
|
|
|
}
|
|
|
|
|
|
|
|
ECSignature.prototype.toScriptSignature = function (hashType) {
|
|
|
|
var hashTypeMod = hashType & ~0x80
|
|
|
|
assert(hashTypeMod > 0x00 && hashTypeMod < 0x04, 'Invalid hashType ' + hashType)
|
|
|
|
|
|
|
|
var hashTypeBuffer = new Buffer(1)
|
|
|
|
hashTypeBuffer.writeUInt8(hashType, 0)
|
|
|
|
|
|
|
|
return Buffer.concat([this.toDER(), hashTypeBuffer])
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = ECSignature
|