165 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 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);
 | |
|   }
 | |
| }
 |