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