diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 0adc2478..35a8a3c9 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -176,6 +176,9 @@ script.execute = function execute(s, stack, tx, index) { if (!constants.rhashType[type & 0x7f]) return false; + if (!script.isValidSig(sig)) + return false; + var subscript = input.out.tx.getSubscript(input.out.index); var hash = tx.subscriptHash(index, subscript, type); @@ -226,6 +229,9 @@ script.execute = function execute(s, stack, tx, index) { var subscript = input.out.tx.getSubscript(input.out.index); var hash = tx.subscriptHash(index, subscript, type); + if (!script.isValidSig(sig)) + return false; + var res = false; for (; !res && j < n; j++) res = script.verify(hash, sig.slice(0, -1), keys[j]); @@ -413,3 +419,98 @@ script.isNullData = function isNullData(s) { Array.isArray(s[1]) && s[1].length <= 40; }; + +// https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki +/** + * A canonical signature exists of: <30> <02> <02> + * Where R and S are not negative (their first byte has its highest bit not set), and not + * excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, + * in which case a single 0 byte is necessary and even required). + * + * See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 + * + * This function is consensus-critical since BIP66. + */ +script.isValidSig = function(sig) { + // Empty signature. Not strictly DER encoded, but allowed to provide a + // compact way to provide an invalid signature for use with CHECK(MULTI)SIG + if (sig.length === 0) + return true; + + // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash] + // * total-length: 1-byte length descriptor of everything that follows, + // excluding the sighash byte. + // * R-length: 1-byte length descriptor of the R value that follows. + // * R: arbitrary-length big-endian encoded R value. It must use the shortest + // possible encoding for a positive integers (which means no null bytes at + // the start, except a single one when the next byte has its highest bit set). + // * S-length: 1-byte length descriptor of the S value that follows. + // * S: arbitrary-length big-endian encoded S value. The same rules apply. + // * sighash: 1-byte value indicating what data is hashed (not part of the DER + // signature) + + // Minimum and maximum size constraints. + if (sig.length < 9) + return false; + if (sig.length > 73) + return false; + + // A signature is of type 0x30 (compound). + if (sig[0] !== 0x30) + return false; + + // Make sure the length covers the entire signature. + if (sig[1] !== sig.length - 3) + return false; + + // Extract the length of the R element. + var lenR = sig[3]; + + // Make sure the length of the S element is still inside the signature. + if (5 + lenR >= sig.length) + return false; + + // Extract the length of the S element. + var lenS = sig[5 + lenR]; + + // Verify that the length of the signature matches the sum of the length + // of the elements. + if (lenR + lenS + 7 !== sig.length) + return false; + + // Check whether the R element is an integer. + if (sig[2] !== 0x02) + return false; + + // Zero-length integers are not allowed for R. + if (lenR === 0) + return false; + + // Negative numbers are not allowed for R. + if (sig[4] & 0x80) + return false; + + // Null bytes at the start of R are not allowed, unless R would + // otherwise be interpreted as a negative number. + if (lenR > 1 && (sig[4] === 0x00) && !(sig[5] & 0x80)) + return false; + + // Check whether the S element is an integer. + if (sig[lenR + 4] !== 0x02) + return false; + + // Zero-length integers are not allowed for S. + if (lenS === 0) + return false; + + // Negative numbers are not allowed for S. + if (sig[lenR + 6] & 0x80) + return false; + + // Null bytes at the start of S are not allowed, unless S would otherwise be + // interpreted as a negative number. + if (lenS > 1 && (sig[lenR + 6] === 0x00) && !(sig[lenR + 7] & 0x80)) + return false; + + return true; +};