diff --git a/src/ecsignature.js b/src/ecsignature.js index abab161..4615ecf 100644 --- a/src/ecsignature.js +++ b/src/ecsignature.js @@ -32,39 +32,63 @@ ECSignature.parseCompact = function (buffer) { } } +// Strict DER - https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki +// NOTE: SIGHASH byte ignored ECSignature.fromDER = function (buffer) { - assert.equal(buffer.readUInt8(0), 0x30, 'Not a DER sequence') - assert.equal(buffer.readUInt8(1), buffer.length - 2, 'Invalid sequence length') - assert.equal(buffer.readUInt8(2), 0x02, 'Expected a DER integer') + // Minimum and maximum size constraints. + if (buffer.length < 9) throw new Error('Invalid sequence length') + if (buffer.length > 73) throw new Error('Invalid sequence length') - var rLen = buffer.readUInt8(3) - assert(rLen > 0, 'R length is zero') + // A signature is of type 0x30 (compound). + if (buffer[0] !== 0x30) throw new Error('Not a DER sequence') - var offset = 4 + rLen - assert.equal(buffer.readUInt8(offset), 0x02, 'Expected a DER integer (2)') + // Make sure the length covers the entire signature. + if (buffer[1] !== buffer.length - 2) throw new Error('Invalid sequence length') - var sLen = buffer.readUInt8(offset + 1) - assert(sLen > 0, 'S length is zero') + // Check whether the R element is an integer. + if (buffer[2] !== 0x02) throw new Error('Expected a DER integer') - var rB = buffer.slice(4, offset) - var sB = buffer.slice(offset + 2) - offset += 2 + sLen + // Extract the length of the R element. + var lenR = buffer.readUInt8(3) - if (rLen > 1 && rB.readUInt8(0) === 0x00) { - assert(rB.readUInt8(1) & 0x80, 'R value excessively padded') - } + // Zero-length integers are not allowed for R. + if (lenR === 0) throw new Error('R length is zero') - if (sLen > 1 && sB.readUInt8(0) === 0x00) { - assert(sB.readUInt8(1) & 0x80, 'S value excessively padded') - } + // Make sure the length of the R element is still inside the signature. + if (5 + lenR >= buffer.length) throw new Error('Invalid DER encoding') - assert.equal(offset, buffer.length, 'Invalid DER encoding') + // Check whether the S element is an integer. + if (buffer[4 + lenR] !== 0x02) throw new Error('Expected a DER integer (2)') + + var lenS = buffer[5 + lenR] + + // Zero-length integers are not allowed for S. + if (lenS === 0) throw new Error('S length is zero') + + // Verify that the length of the signature matches the sum of the length + // of the elements. + if ((lenR + lenS + 6) !== buffer.length) throw new Error('Invalid DER encoding (2)') + + // Negative numbers are not allowed for R. + if (buffer[4] & 0x80) throw new Error('R value is negative') + + // Null bytes at the start of R are not allowed, unless R would + // otherwise be interpreted as a negative number. + if (lenR > 1 && (buffer[4] === 0x00) && !(buffer[5] & 0x80)) throw new Error('R value excessively padded') + + // Negative numbers are not allowed for S. + if (buffer[lenR + 6] & 0x80) throw new Error('S value is negative') + + // Null bytes at the start of S are not allowed, unless S would otherwise be + // interpreted as a negative number. + 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) - assert(r.signum() >= 0, 'R value is negative') - assert(s.signum() >= 0, 'S value is negative') - return new ECSignature(r, s) } diff --git a/test/ecsignature.js b/test/ecsignature.js index aec93a1..e1fd055 100644 --- a/test/ecsignature.js +++ b/test/ecsignature.js @@ -66,7 +66,7 @@ describe('ECSignature', function () { }) fixtures.invalid.DER.forEach(function (f) { - it('throws on ' + f.hex, function () { + it('throws "' + f.exception + '" for ' + f.hex, function () { var buffer = new Buffer(f.hex, 'hex') assert.throws(function () { diff --git a/test/fixtures/ecsignature.json b/test/fixtures/ecsignature.json index 2c72182..7f11188 100644 --- a/test/fixtures/ecsignature.json +++ b/test/fixtures/ecsignature.json @@ -147,7 +147,11 @@ }, { "exception": "Invalid DER encoding", - "hex": "300c020400ffffff020200ffffff" + "hex": "300c02040fffffff020200ffffff" + }, + { + "exception": "Invalid DER encoding \\(2\\)", + "hex": "300c020400ffffff020800ffffff" }, { "exception": "R length is zero",