var BN = require('bn.js');
|
|
var MillerRabin = require('miller-rabin');
|
|
var millerRabin = new MillerRabin();
|
|
var TWENTYFOUR = new BN(24);
|
|
var ELEVEN = new BN(11);
|
|
var TEN = new BN(10);
|
|
var THREE = new BN(3);
|
|
var SEVEN = new BN(7);
|
|
var primes = require('./generatePrime');
|
|
var randomBytes = require('randombytes');
|
|
module.exports = DH;
|
|
|
|
function setPublicKey(pub, enc) {
|
|
enc = enc || 'utf8';
|
|
if (!Buffer.isBuffer(pub)) {
|
|
pub = new Buffer(pub, enc);
|
|
}
|
|
this._pub = new BN(pub);
|
|
return this;
|
|
}
|
|
|
|
function setPrivateKey(priv, enc) {
|
|
enc = enc || 'utf8';
|
|
if (!Buffer.isBuffer(priv)) {
|
|
priv = new Buffer(priv, enc);
|
|
}
|
|
this._priv = new BN(priv);
|
|
return this;
|
|
}
|
|
|
|
var primeCache = {};
|
|
function checkPrime(prime, generator) {
|
|
var gen = generator.toString('hex');
|
|
var hex = [gen, prime.toString(16)].join('_');
|
|
if (hex in primeCache) {
|
|
return primeCache[hex];
|
|
}
|
|
var error = 0;
|
|
|
|
if (prime.isEven() ||
|
|
!primes.simpleSieve ||
|
|
!primes.fermatTest(prime) ||
|
|
!millerRabin.test(prime)) {
|
|
//not a prime so +1
|
|
error += 1;
|
|
|
|
if (gen === '02' || gen === '05') {
|
|
// we'd be able to check the generator
|
|
// it would fail so +8
|
|
error += 8;
|
|
} else {
|
|
//we wouldn't be able to test the generator
|
|
// so +4
|
|
error += 4;
|
|
}
|
|
primeCache[hex] = error;
|
|
return error;
|
|
}
|
|
if (!millerRabin.test(prime.shrn(1))) {
|
|
//not a safe prime
|
|
error += 2;
|
|
}
|
|
var rem;
|
|
switch (gen) {
|
|
case '02':
|
|
if (prime.mod(TWENTYFOUR).cmp(ELEVEN)) {
|
|
// unsuidable generator
|
|
error += 8;
|
|
}
|
|
break;
|
|
case '05':
|
|
rem = prime.mod(TEN);
|
|
if (rem.cmp(THREE) && rem.cmp(SEVEN)) {
|
|
// prime mod 10 needs to equal 3 or 7
|
|
error += 8;
|
|
}
|
|
break;
|
|
default:
|
|
error += 4;
|
|
}
|
|
primeCache[hex] = error;
|
|
return error;
|
|
}
|
|
|
|
function DH(prime, generator, malleable) {
|
|
this.setGenerator(generator);
|
|
this.__prime = new BN(prime);
|
|
this._prime = BN.mont(this.__prime);
|
|
this._primeLen = prime.length;
|
|
this._pub = undefined;
|
|
this._priv = undefined;
|
|
this._primeCode = undefined;
|
|
if (malleable) {
|
|
this.setPublicKey = setPublicKey;
|
|
this.setPrivateKey = setPrivateKey;
|
|
} else {
|
|
this._primeCode = 8;
|
|
}
|
|
}
|
|
Object.defineProperty(DH.prototype, 'verifyError', {
|
|
enumerable: true,
|
|
get: function () {
|
|
if (typeof this._primeCode !== 'number') {
|
|
this._primeCode = checkPrime(this.__prime, this.__gen);
|
|
}
|
|
return this._primeCode;
|
|
}
|
|
});
|
|
DH.prototype.generateKeys = function () {
|
|
if (!this._priv) {
|
|
this._priv = new BN(randomBytes(this._primeLen));
|
|
}
|
|
this._pub = this._gen.toRed(this._prime).redPow(this._priv).fromRed();
|
|
return this.getPublicKey();
|
|
};
|
|
|
|
DH.prototype.computeSecret = function (other) {
|
|
other = new BN(other);
|
|
other = other.toRed(this._prime);
|
|
var secret = other.redPow(this._priv).fromRed();
|
|
var out = new Buffer(secret.toArray());
|
|
var prime = this.getPrime();
|
|
if (out.length < prime.length) {
|
|
var front = new Buffer(prime.length - out.length);
|
|
front.fill(0);
|
|
out = Buffer.concat([front, out]);
|
|
}
|
|
return out;
|
|
};
|
|
|
|
DH.prototype.getPublicKey = function getPublicKey(enc) {
|
|
return formatReturnValue(this._pub, enc);
|
|
};
|
|
|
|
DH.prototype.getPrivateKey = function getPrivateKey(enc) {
|
|
return formatReturnValue(this._priv, enc);
|
|
};
|
|
|
|
DH.prototype.getPrime = function (enc) {
|
|
return formatReturnValue(this.__prime, enc);
|
|
};
|
|
|
|
DH.prototype.getGenerator = function (enc) {
|
|
return formatReturnValue(this._gen, enc);
|
|
};
|
|
|
|
DH.prototype.setGenerator = function (gen, enc) {
|
|
enc = enc || 'utf8';
|
|
if (!Buffer.isBuffer(gen)) {
|
|
gen = new Buffer(gen, enc);
|
|
}
|
|
this.__gen = gen;
|
|
this._gen = new BN(gen);
|
|
return this;
|
|
};
|
|
|
|
function formatReturnValue(bn, enc) {
|
|
var buf = new Buffer(bn.toArray());
|
|
if (!enc) {
|
|
return buf;
|
|
} else {
|
|
return buf.toString(enc);
|
|
}
|
|
}
|