schnorr: add hmac-drbg. refactor.
This commit is contained in:
parent
33ffb780b3
commit
68a3209a96
123
lib/crypto/hmac-drbg.js
Normal file
123
lib/crypto/hmac-drbg.js
Normal 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;
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
"dependencies": {
|
||||
"bn.js": "4.11.6",
|
||||
"elliptic": "6.4.0",
|
||||
"hmac-drbg": "^1.0.0",
|
||||
"n64": "0.0.11"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user