| 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); |
| } |
| } |