Merge pull request #167 from matiu/feature/IsCanonicalSignature
add isCanonicalSignature check to script interpreter and tests
This commit is contained in:
commit
56cb3ffa9a
|
@ -7,6 +7,11 @@ var bignum = imports.bignum || require('bignum');
|
|||
var Util = imports.Util || require('./util/util');
|
||||
var Script = require('./Script');
|
||||
|
||||
var SIGHASH_ALL = 1;
|
||||
var SIGHASH_NONE = 2;
|
||||
var SIGHASH_SINGLE = 3;
|
||||
var SIGHASH_ANYONECANPAY = 80;
|
||||
|
||||
// Make opcodes available as pseudo-constants
|
||||
for (var i in Opcode.map) {
|
||||
eval(i + " = " + Opcode.map[i] + ";");
|
||||
|
@ -607,6 +612,9 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
|||
// Remove signature if present (a signature can't sign itself)
|
||||
scriptCode.findAndDelete(sig);
|
||||
|
||||
//
|
||||
isCanonicalSignature(new Buffer(sig));
|
||||
|
||||
// Verify signature
|
||||
checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) {
|
||||
try {
|
||||
|
@ -680,6 +688,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
|||
|
||||
// Drop the signatures, since a signature can't sign itself
|
||||
sigs.forEach(function(sig) {
|
||||
isCanonicalSignature(new Buffer(sig));
|
||||
scriptCode.findAndDelete(sig);
|
||||
});
|
||||
|
||||
|
@ -1056,4 +1065,68 @@ var checkSig = ScriptInterpreter.checkSig =
|
|||
}
|
||||
};
|
||||
|
||||
var isCanonicalSignature = ScriptInterpreter.isCanonicalSignature = function(sig, opts) {
|
||||
// See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
|
||||
// A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
|
||||
// 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).
|
||||
|
||||
if (!Buffer.isBuffer(sig))
|
||||
throw new Error("arg should be a Buffer");
|
||||
|
||||
opts = opts || {};
|
||||
|
||||
var l = sig.length;
|
||||
if (l < 9) throw new Error("Non-canonical signature: too short");
|
||||
if (l > 73) throw new Error("Non-canonical signature: too long");
|
||||
|
||||
var nHashType = sig[l-1] & (~(SIGHASH_ANYONECANPAY));
|
||||
if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE)
|
||||
throw new Error("Non-canonical signature: unknown hashtype byte");
|
||||
|
||||
if (sig[0] !== 0x30)
|
||||
throw new Error("Non-canonical signature: wrong type");
|
||||
if (sig[1] !== l-3)
|
||||
throw new Error("Non-canonical signature: wrong length marker");
|
||||
|
||||
var nLenR = sig[3];
|
||||
if (5 + nLenR >= l)
|
||||
throw new Error("Non-canonical signature: S length misplaced");
|
||||
|
||||
var nLenS = sig[5+nLenR];
|
||||
if ( (nLenR+nLenS+7) !== l)
|
||||
throw new Error("Non-canonical signature: R+S length mismatch");
|
||||
|
||||
var rPos = 4;
|
||||
var R = new Buffer(nLenR);
|
||||
sig.copy(R, 0, rPos, rPos+ nLenR);
|
||||
if (sig[rPos-2] !== 0x02)
|
||||
throw new Error("Non-canonical signature: R value type mismatch");
|
||||
if (nLenR == 0)
|
||||
throw new Error("Non-canonical signature: R length is zero");
|
||||
if (R[0] & 0x80)
|
||||
throw new Error("Non-canonical signature: R value negative");
|
||||
if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80))
|
||||
throw new Error("Non-canonical signature: R value excessively padded");
|
||||
|
||||
var sPos = 6 + nLenR;
|
||||
var S = new Buffer(nLenS);
|
||||
sig.copy(S, 0, sPos, sPos+ nLenS);
|
||||
if (sig[sPos-2] != 0x02)
|
||||
throw new Error("Non-canonical signature: S value type mismatch");
|
||||
if (nLenS == 0)
|
||||
throw new Error("Non-canonical signature: S length is zero");
|
||||
if (S[0] & 0x80)
|
||||
throw new Error("Non-canonical signature: S value negative");
|
||||
if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80))
|
||||
throw new Error("Non-canonical signature: S value excessively padded");
|
||||
|
||||
if (opts.verifyEvenS) {
|
||||
if (S[nLenS-1] & 1)
|
||||
throw new Error("Non-canonical signature: S value odd");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = require('soop')(ScriptInterpreter);
|
||||
|
|
|
@ -42,6 +42,30 @@ describe('ScriptInterpreter', function() {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
var i = 0;
|
||||
testdata.dataSigCanonical.forEach(function(datum) {
|
||||
it('should validate valid canonical signatures', function() {
|
||||
ScriptInterpreter.isCanonicalSignature(new Buffer(datum,'hex')).should.equal(true);
|
||||
});
|
||||
});
|
||||
testdata.dataSigNonCanonical.forEach(function(datum) {
|
||||
it('should NOT validate invalid canonical signatures', function() {
|
||||
|
||||
var sig;
|
||||
var isHex;
|
||||
//is Hex?
|
||||
try {
|
||||
sig =new Buffer(datum,'hex');
|
||||
isHex=1;
|
||||
} catch (e) { }
|
||||
|
||||
if (isHex)
|
||||
ScriptInterpreter.isCanonicalSignature.bind(sig).should.throw();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ var dataTxValid = JSON.parse(fs.readFileSync('test/data/tx_valid.json'));
|
|||
var dataTxInvalid = JSON.parse(fs.readFileSync('test/data/tx_invalid.json'));
|
||||
var dataScriptValid = JSON.parse(fs.readFileSync('test/data/script_valid.json'));
|
||||
var dataScriptInvalid = JSON.parse(fs.readFileSync('test/data/script_invalid.json'));
|
||||
var dataSigCanonical = JSON.parse(fs.readFileSync('test/data/sig_canonical.json'));
|
||||
var dataSigNonCanonical = JSON.parse(fs.readFileSync('test/data/sig_noncanonical.json'));
|
||||
|
||||
module.exports.dataValid = dataValid;
|
||||
module.exports.dataInvalid = dataInvalid;
|
||||
|
@ -16,3 +18,6 @@ module.exports.dataTxInvalid = dataTxInvalid;
|
|||
module.exports.dataScriptValid = dataScriptValid;
|
||||
module.exports.dataScriptInvalid = dataScriptInvalid;
|
||||
module.exports.dataScriptAll = dataScriptValid.concat(dataScriptInvalid);
|
||||
|
||||
module.exports.dataSigCanonical = dataSigCanonical;
|
||||
module.exports.dataSigNonCanonical = dataSigNonCanonical;
|
||||
|
|
Loading…
Reference in New Issue