var assert = require('assert') var typeForce = require('typeforce') var BigInteger = require('bigi') function ECSignature (r, s) { typeForce('BigInteger', r) typeForce('BigInteger', 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 assert(hashTypeMod > 0x00 && hashTypeMod < 0x04, '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