diff --git a/lib/bcoin.js b/lib/bcoin.js index 8dbf56c9..a7456309 100644 --- a/lib/bcoin.js +++ b/lib/bcoin.js @@ -31,5 +31,6 @@ bcoin.protocol.network.set(process.env.BCOIN_NETWORK || 'main'); bcoin.bn = bn; bcoin.elliptic = elliptic; +bcoin.signature = require('elliptic/lib/elliptic/ec/signature'); bcoin.hash = hash; bcoin.async = async; diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 99882b55..e9f5ac24 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -237,9 +237,12 @@ script.subscript = function subscript(s, lastSep) { return res; }; -script.checksig = function checksig(hash, sig, pub) { +script.checksig = function checksig(msg, sig, pub) { var k; + if (pub.getPublic) + pub = pub.getPublic(); + try { k = bcoin.ecdsa.keyPair({ pub: pub }); } catch (e) { @@ -255,7 +258,7 @@ script.checksig = function checksig(hash, sig, pub) { // Use a try catch in case there are // any uncaught errors for bad inputs in verify(). try { - return bcoin.ecdsa.verify(hash, sig, pub); + return bcoin.ecdsa.verify(msg, sig, pub); } catch (e) { return false; } @@ -1542,6 +1545,54 @@ script.isValidSignature = function isValidSignature(sig, allowZero) { return true; }; +script.isLowDER = function isLowDER(sig, type) { + var half = bcoin.ecdsa.n.ushrn(1); + + if (!sig.s) { + assert(utils.isBuffer(sig)); + + if (sig[sig.length - 1] === type) + sig = sig.slice(0, -1); + + try { + sig = new bcoin.signature(sig); + } catch (e) { + return false; + } + } + + // Technically a negative S value is low, + // but we don't want to ever use negative + // S values in bitcoin. + if (sig.s.cmpn(0) <= 0) + return false; + + // If S is greater than half the order, + // it's too high. + if (sig.s.cmp(half) > 0) + return false; + + return true; +}; + +script.sign = function sign(msg, key) { + var half = bcoin.ecdsa.n.ushrn(1); + var sig = bcoin.ecdsa.sign(msg, key.priv); + + // Elliptic shouldn't be generating + // negative S values. + assert(sig.s.cmpn(0) > 0); + + // S value is already low. + if (sig.s.cmp(half) <= 0) + return sig.toDER(); + + // Subtract from the `n` order to make it smaller. + sig.s = bcoin.ecdsa.n.sub(sig.s); + + return sig.toDER(); +}; + script.format = function format(input, output) { var scripts = []; var prev, redeem; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 4a838c35..2a3b7907 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -251,7 +251,10 @@ TX.prototype.signInput = function signInput(index, key, type) { hash = this.signatureHash(index, redeem, type); // Sign the transaction with our one input - signature = bcoin.ecdsa.sign(hash, key.priv).toDER(); + signature = bcoin.script.sign(hash, key); + + // Something is broken if this doesn't work: + assert(bcoin.script.checksig(hash, signature, key)); // Add the sighash as a single byte to the signature signature = signature.concat(type);