schnorr: add hmac-drbg. refactor.

This commit is contained in:
Christopher Jeffrey 2017-06-22 05:22:06 -07:00
parent 33ffb780b3
commit 68a3209a96
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 184 additions and 123 deletions

123
lib/crypto/hmac-drbg.js Normal file
View File

@ -0,0 +1,123 @@
/*!
* hmac-drbg.js - hmac-drbg implementation for bcoin
* Copyright (c) 2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
* Parts of this software based on hmac-drbg.
*/
'use strict';
var assert = require('assert');
var backend = require('./backend');
/*
* Constants
*/
var HASH_ALG = 'sha256';
var HASH_SIZE = 32;
var RESEED_INTERVAL = 0x1000000000000;
var POOL33 = Buffer.allocUnsafe(HASH_SIZE + 1);
var POOL112 = Buffer.allocUnsafe(HASH_SIZE * 2 + 48);
var POOL145 = Buffer.allocUnsafe(POOL33.length + POOL112.length);
/**
* HmacDRBG
* @constructor
*/
function HmacDRBG(entropy, nonce, pers) {
if (!(this instanceof HmacDRBG))
return new HmacDRBG(entropy, nonce, pers);
this.K = Buffer.allocUnsafe(HASH_SIZE);
this.V = Buffer.allocUnsafe(HASH_SIZE);
this.rounds = 0;
this.init(entropy, nonce, pers);
}
HmacDRBG.prototype.init = function init(entropy, nonce, pers) {
var i;
for (i = 0; i < this.V.length; i++) {
this.K[i] = 0x00;
this.V[i] = 0x01;
}
this.reseed(entropy, nonce, pers);
};
HmacDRBG.prototype.reseed = function reseed(entropy, nonce, pers) {
var seed = POOL112;
var i;
assert(Buffer.isBuffer(entropy));
assert(Buffer.isBuffer(nonce));
assert(Buffer.isBuffer(pers));
assert(entropy.length === HASH_SIZE);
assert(nonce.length === HASH_SIZE);
assert(pers.length === 48);
entropy.copy(seed, 0);
nonce.copy(seed, HASH_SIZE);
pers.copy(seed, HASH_SIZE * 2);
this.update(seed);
this.rounds = 1;
};
HmacDRBG.prototype.iterate = function iterate() {
var data = POOL33;
this.V.copy(data, 0);
data[HASH_SIZE] = 0x00;
this.K = backend.hmac(HASH_ALG, data, this.K);
this.V = backend.hmac(HASH_ALG, this.V, this.K);
};
HmacDRBG.prototype.update = function update(seed) {
var data = POOL145;
assert(Buffer.isBuffer(seed));
assert(seed.length === HASH_SIZE * 2 + 48);
this.V.copy(data, 0);
data[HASH_SIZE] = 0x00;
seed.copy(data, HASH_SIZE + 1);
this.K = backend.hmac(HASH_ALG, data, this.K);
this.V = backend.hmac(HASH_ALG, this.V, this.K);
data[HASH_SIZE] = 0x01;
this.K = backend.hmac(HASH_ALG, data, this.K);
this.V = backend.hmac(HASH_ALG, this.V, this.K);
};
HmacDRBG.prototype.generate = function generate(len) {
var data = Buffer.allocUnsafe(len);
var pos = 0;
if (this.rounds > RESEED_INTERVAL)
throw new Error('Reseed is required.');
while (pos < len) {
this.V = backend.hmac(HASH_ALG, this.V, this.K);
this.V.copy(data, pos);
pos += HASH_SIZE;
}
this.iterate();
this.rounds++;
return data;
};
/*
* Expose
*/
module.exports = HmacDRBG;

View File

@ -8,13 +8,14 @@
var elliptic = require('elliptic');
var Signature = require('elliptic/lib/elliptic/ec/signature');
var hmacDRBG = require('hmac-drbg');
var BN = require('./bn');
var HmacDRBG = require('./hmac-drbg');
var sha256 = require('./backend').sha256;
var secp256k1 = elliptic.ec('secp256k1');
var curve = secp256k1.curve;
var curves = elliptic.curves;
var hash = curves.secp256k1.hash;
var POOL64 = Buffer.allocUnsafe(64);
/**
* @exports crypto/schnorr
@ -26,23 +27,17 @@ var schnorr = exports;
* Hash (r | M).
* @param {Buffer} msg
* @param {BN} r
* @param {Function?} hash
* @returns {Buffer}
*/
schnorr.hash = function _hash(msg, r, hash) {
schnorr.hash = function _hash(msg, r) {
var R = r.toArrayLike(Buffer, 'be', 32);
var B = Buffer.allocUnsafe(64);
var H;
if (!hash)
hash = sha256;
var B = POOL64;
R.copy(B, 0);
msg.copy(B, 32);
H = hash(B);
return new BN(H);
return new BN(sha256(B));
};
/**
@ -51,46 +46,49 @@ schnorr.hash = function _hash(msg, r, hash) {
* @param {Buffer} msg
* @param {BN} priv
* @param {BN} k
* @param {Function|null} hash
* @param {Buffer} pubnonce
* @param {Buffer} pn
* @returns {Signature|null}
*/
schnorr._sign = function _sign(msg, prv, k, hash, pubnonce) {
schnorr.trySign = function trySign(msg, prv, k, pn) {
var r, pn, h, s;
if (prv.cmpn(0) === 0)
throw new Error('Bad private key.');
if (prv.cmp(curve.n) >= 0)
throw new Error('Bad private key.');
if (k.cmpn(0) === 0)
return;
return null;
if (k.cmp(curve.n) >= 0)
return;
return null;
r = curve.g.mul(k);
if (pubnonce) {
pn = curve.decodePoint(pubnonce);
if (pn)
r = r.add(pn);
}
if (r.y.isOdd()) {
k = k.umod(curve.n);
k = curve.n.sub(k);
}
h = schnorr.hash(msg, r.getX(), hash);
h = schnorr.hash(msg, r.getX());
if (h.cmpn(0) === 0)
return;
return null;
if (h.cmp(curve.n) >= 0)
return;
return null;
s = h.imul(prv);
s = k.isub(s);
s = s.umod(curve.n);
if (s.cmpn(0) === 0)
return;
return null;
return new Signature({ r: r.getX(), s: s });
};
@ -99,26 +97,22 @@ schnorr._sign = function _sign(msg, prv, k, hash, pubnonce) {
* Sign message.
* @param {Buffer} msg
* @param {Buffer} key
* @param {Function?} hash
* @param {Buffer} pubnonce
* @param {Buffer} pubNonce
* @returns {Signature}
*/
schnorr.sign = function sign(msg, key, hash, pubnonce) {
schnorr.sign = function sign(msg, key, pubNonce) {
var prv = new BN(key);
var k, sig, drbg;
var drbg = schnorr.drbg(msg, key, pubNonce);
var len = curve.n.byteLength();
var k, pn, sig;
if (prv.cmpn(0) === 0)
throw new Error('Bad private key.');
if (prv.cmp(curve.n) >= 0)
throw new Error('Bad private key.');
drbg = schnorr.drbg(msg, key, pubnonce);
if (pubNonce)
pn = curve.decodePoint(pubNonce);
while (!sig) {
k = new BN(drbg.generate(curve.n.byteLength()));
sig = schnorr._sign(msg, prv, k, hash, pubnonce);
k = new BN(drbg.generate(len));
sig = schnorr.trySign(msg, prv, k, pn);
}
return sig;
@ -129,13 +123,12 @@ schnorr.sign = function sign(msg, key, hash, pubnonce) {
* @param {Buffer} msg
* @param {Buffer} signature
* @param {Buffer} key
* @param {Function?} hash
* @returns {Buffer}
*/
schnorr.verify = function verify(msg, signature, key, hash) {
schnorr.verify = function verify(msg, signature, key) {
var sig = new Signature(signature);
var h = schnorr.hash(msg, sig.r, hash);
var h = schnorr.hash(msg, sig.r);
var k, l, r, rl;
if (h.cmp(curve.n) >= 0)
@ -165,13 +158,12 @@ schnorr.verify = function verify(msg, signature, key, hash) {
* Recover public key.
* @param {Buffer} msg
* @param {Buffer} signature
* @param {Function?} hash
* @returns {Buffer}
*/
schnorr.recover = function recover(signature, msg, hash) {
schnorr.recover = function recover(signature, msg) {
var sig = new Signature(signature);
var h = schnorr.hash(msg, sig.r, hash);
var h = schnorr.hash(msg, sig.r);
var hinv, s, R, l, r, k, rl;
if (h.cmp(curve.n) >= 0)
@ -280,23 +272,16 @@ schnorr.combineKeys = function combineKeys(keys) {
* Partially sign.
* @param {Buffer} msg
* @param {Buffer} priv
* @param {Buffer} privnonce
* @param {Buffer} pubs
* @param {Function?} hash
* @param {Buffer} privNonce
* @param {Buffer} pubNonce
* @returns {Buffer}
*/
schnorr.partialSign = function partialSign(msg, priv, privnonce, pubs, hash) {
schnorr.partialSign = function partialSign(msg, priv, privNonce, pubNonce) {
var prv = new BN(priv);
var sig;
if (prv.cmpn(0) === 0)
throw new Error('Bad private key.');
if (prv.cmp(curve.n) >= 0)
throw new Error('Bad private key.');
sig = schnorr._sign(msg, prv, new BN(privnonce), hash, pubs);
var k = new BN(privNonce);
var pn = curve.decodePoint(pubNonce);
var sig = schnorr.trySign(msg, prv, k, pn);
if (!sig)
throw new Error('Bad K value.');
@ -320,63 +305,18 @@ schnorr.alg = Buffer.from('Schnorr+SHA256 ', 'ascii');
*/
schnorr.drbg = function drbg(msg, priv, data) {
var kdata = Buffer.allocUnsafe(112);
var prv, pers;
var pers = Buffer.allocUnsafe(48);
kdata.fill(0);
pers.fill(0);
priv.copy(kdata, 0);
msg.copy(kdata, 32);
if (data) {
assert(data.length === 32);
data.copy(pers, 0);
}
if (data)
data.copy(kdata, 64);
schnorr.alg.copy(pers, 32);
schnorr.alg.copy(kdata, 96);
prv = toArray(kdata.slice(0, 32));
msg = toArray(kdata.slice(32, 64));
pers = toArray(kdata.slice(64));
return new hmacDRBG({
hash: hash,
entropy: prv,
nonce: msg,
pers: pers
});
};
/**
* 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 drbg = schnorr.drbg(msg, priv, data);
var bytes = drbg.generate(curve.n.byteLength());
return Buffer.from(bytes);
};
/**
* 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);
return new HmacDRBG(priv, msg, pers);
};
/**
@ -384,26 +324,25 @@ schnorr.nonce = function nonce(msg, priv, data, ncb) {
* @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);
schnorr.generateNoncePair = function generateNoncePair(msg, priv, data) {
var drbg = schnorr.drbg(msg, priv, data);
var len = curve.n.byteLength();
var k;
if (k.cmpn(0) === 0)
throw new Error('Bad nonce.');
for (;;) {
k = new BN(drbg.generate(len));
if (k.cmp(curve.n) >= 0)
throw new Error('Bad nonce.');
if (k.cmpn(0) === 0)
continue;
if (k.cmp(curve.n) >= 0)
continue;
break;
}
return Buffer.from(curve.g.mul(k).encode('array', true));
};
/*
* Helpers
*/
function toArray(obj) {
return Array.prototype.slice.call(obj);
}

View File

@ -25,7 +25,6 @@
"dependencies": {
"bn.js": "4.11.6",
"elliptic": "6.4.0",
"hmac-drbg": "^1.0.0",
"n64": "0.0.11"
},
"optionalDependencies": {