diff --git a/lib/crypto/schnorr.js b/lib/crypto/schnorr.js index abfd26f6..66f81348 100644 --- a/lib/crypto/schnorr.js +++ b/lib/crypto/schnorr.js @@ -9,6 +9,7 @@ var bn = require('bn.js'); var elliptic = require('elliptic'); var Signature = require('elliptic/lib/elliptic/ec/signature'); +var hmacDRBG = require('elliptic/lib/elliptic/hmac-drbg'); var ec = require('./ec'); var curve = elliptic.ec('secp256k1').curve; var sha256 = require('./crypto').sha256; @@ -230,7 +231,7 @@ schnorr.combineSigs = function combineSigs(sigs) { if (last && last.r.cmp(sig.r) !== 0) throw new Error('Bad signature combination.'); - s.iadd(sig.s); + s = s.iadd(sig.s); s = s.umod(curve.n); last = sig; @@ -279,6 +280,7 @@ schnorr.combineKeys = function combineKeys(keys) { schnorr.partialSign = function partialSign(msg, priv, privnonce, pubs, hash) { var prv = new bn(priv); + var sig; if (prv.cmpn(0) === 0) throw new Error('Bad private key.'); @@ -286,5 +288,104 @@ schnorr.partialSign = function partialSign(msg, priv, privnonce, pubs, hash) { if (prv.cmp(curve.n) >= 0) throw new Error('Bad private key.'); - return schnorr._sign(msg, prv, new bn(privnonce), hash, pubs); + sig = schnorr._sign(msg, prv, new bn(privnonce), hash, pubs); + + if (!sig) + throw new Error('Bad K value.'); + + return sig; }; + +/** + * Schnorr personalization string. + * @const {Buffer} + */ + +schnorr.alg = new Buffer('Schnorr+SHA256 ', 'ascii'); + +/** + * Perform hmac drbg according to rfc6979. + * @param {Buffer} msg + * @param {Buffer} priv + * @param {Buffer} data + * @returns {Buffer} + */ + +schnorr.rfc6979 = function rfc6979(msg, priv, data) { + var kdata = new Buffer(112); + var drbg, prv, pers; + + kdata.fill(0); + + priv.copy(kdata, 0); + msg.copy(kdata, 32); + + if (data) + data.copy(kdata, 64); + + schnorr.alg.copy(kdata, 96); + + prv = toArray(kdata.slice(0, 32)); + msg = toArray(kdata.slice(32, 64)); + pers = toArray(kdata.slice(64)); + + drbg = new hmacDRBG({ + hash: require('hash.js').sha256, + entropy: prv, + nonce: msg, + pers: pers + }); + + drbg = drbg.generate(curve.n.byteLength()); + + return new Buffer(drbg); +}; + +/** + * Create a schnorr nonce with a nonce callback. + * @param {Buffer} msg + * @param {Buffer} priv + * @param {Buffer} data + * @param {Function?} ncb + * @returns {BN} + */ + +schnorr.nonce = function nonce(msg, priv, data, ncb) { + var pubnonce; + + if (!ncb) + ncb = schnorr.rfc6979; + + pubnonce = ncb(msg, priv, data); + + return new bn(pubnonce); +}; + +/** + * Generate pub+priv nonce pair. + * @param {Buffer} msg + * @param {Buffer} priv + * @param {Buffer} data + * @param {Function?} ncb + * @returns {Buffer} + */ + +schnorr.generateNoncePair = function generateNoncePair(msg, priv, data, ncb) { + var k = schnorr.nonce(priv, msg, data, ncb); + + if (k.cmpn(0) === 0) + throw new Error('Bad nonce.'); + + if (k.cmp(curve.n) >= 0) + throw new Error('Bad nonce.'); + + return new Buffer(curve.g.mul(k).encode('array', true)); +}; + +/* + * Helpers + */ + +function toArray(obj) { + return Array.prototype.slice.call(obj); +}