/** * @preserve Copyright (c) 2014-2015 IONU Security, Inc. All rights reserved. * * @file EyeDRBG methods for deterministic random bit generation and predictable RSA keys */ IONU_DRBG_BUFFER_LEN = 32768; if(typeof BigInteger === 'undefined') { var BigInteger = forge.jsbn.BigInteger; } /** * Constructor for DRBG object * @constructor * @param {string} seed value */ function EyeDRBG (seed) { if (!(this instanceof EyeDRBG)){ return new EyeDRBG (seed); } this.seed = seed; this.ctr = seed.length & 255; this.buffer = ''; this.pos = 0; var md = forge.md.sha256.create(); md.update(seed); this.hashbuf = md.digest().getBytes(); //console.log (forge.util.bytesToHex(this.hashbuf)); } // Define methods for EyeDRBG object EyeDRBG.prototype = { constructor: EyeDRBG, /** * Fill the buffer with pseudo random bytes * @private */ fillBuffer: function() { const BUFF_SIZE = IONU_DRBG_BUFFER_LEN; this.buffer = ''; this.pos = 0; var msg = ''; var md = forge.md.sha256.create(); for (var i = 0; i < BUFF_SIZE; i += 32) { msg = ''; md.start(); for (var j = 0; j < 32; ++j) { msg += String.fromCharCode (this.hashbuf.charCodeAt(j)); } msg += String.fromCharCode(this.ctr++ & 255); md.update(msg); this.hashbuf = md.digest().getBytes(); this.buffer += this.hashbuf; //console.log (forge.util.bytesToHex(this.hashbuf)); } }, /** * Get the specifed number of bytes * @param {number} count bytes to get * @return {string} byte string */ randBytes: function (count) { var bytes = ''; var bytesavailable = this.buffer.length - this.pos; // We have all the bytes needed if (bytesavailable >= count) { bytes = this.buffer.slice(this.pos, this.pos + count); this.pos += count; } else { // Copy any remaining bytes if (bytesavailable > 0) { count -= bytesavailable; bytes = this.buffer.slice (this.pos, bytesavailable); } // Loop until we've generated all the required bytes while (count > 0) { this.fillBuffer (); bytesavailable = this.buffer.length - this.pos; if (bytesavailable >= count ) { bytes += this.buffer.slice(this.pos, this.pos + count); this.pos += count; count = 0; } else { count -= bytesavailable; bytes += this.buffer.slice (this.pos, bytesavailable); } } } return bytes; }, /** * Get an integer with the specifed number of bits * @param {number} bits number of bits to get * @return {number} integer of specified number of bits */ randBits: function (bits) { var rval = 0; var numBytes = Math.floor ((bits + 7) / 8); for (i = 0; i < numBytes; i++) { rval = (rval << 8) | this.randBytes (1); } return rval & (1 << bits); }, /** * Get an integer from 0 to the specifed maximum * @param {number} maximum largest integer to get * @return {number} integer between 0 and maximum, inclusive */ randInteger: function (maximum) { var n = 1, numBits = 0; // numBits = log_2 n, where n >= maximum while (maximum >= n) { if (numBits > 0) { n <<= 1; } numBits++; } // Generate random values from 0 through n, then discard any values where value > maximum. // See Ferguson and Schneier, "Practical Cryptography" for why this is better than using mod while (true) { var value = this.randBits (numBits); if (value <= n) { return value; } } }, /** * Generate pseudo prime suitable for RSA key * @param {BigInteger} exponent public exponent (65,537) * @return {BigInteger} prime number */ generateRSAPrime: function (e) { // Only works for IONU_RSA_KEY_BITS size keys, prime is IONU_RSA_KEY_BITS / 2 var bytes = IONU_RSA_KEY_BITS / 16; var op_or = function(x,y) { return x|y; }; var THIRTY = new BigInteger(null); THIRTY.fromInt(30); var iterations = 0; while (true) { buff = this.randBytes (bytes); // Get bytes from our DRBG var hex = forge.util.bytesToHex(buff); var prime = new BigInteger (hex, 16); // Set 2 most significant bits so p * q is exactly n bits, and least to ensure odd var bits = IONU_RSA_KEY_BITS / 2 - 1; prime.bitwiseTo (BigInteger.ONE.shiftLeft(bits), op_or, prime); prime.bitwiseTo (BigInteger.ONE.shiftLeft(bits-1), op_or, prime); prime.bitwiseTo (BigInteger.ONE, op_or, prime); if (++iterations >= 2000) { console.log ('prime failed in 2000 iterations'); return prime; } // Ensure number is coprime with e if (prime.subtract(BigInteger.ONE).gcd(e).compareTo(BigInteger.ONE) === 0) { // Check to see if it is prime if (prime.isProbablePrime(2)) { //console.log ('prime in ' + iterations + ' iterations'); return prime; } } } }, /** * Generate deterministic RSA key * @return {Key} */ generateRSAKey: function () { // Set up public exponent var eInt = IONU_RSA_PUBLIC_EXP; var e = new BigInteger(null); e.fromInt(eInt); // Generate random primes suitable for RSA keys, proper size, odd numbers and // checking for coprime with e is done in generateRSAPrime. var p = this.generateRSAPrime (e); var q = this.generateRSAPrime (e); while (p.compareTo (q) === 0) { // Just is case they are equal q = this.generateRSAPrime (e); if (p.compareTo (q) === 0) { // Equal again, it's a bug, don't hang console.log ('generateRSAKey() - generated the same p and q'); return undefined; } } // Ensure p is larger than q (swap them if not for performance) if(p.compareTo(q) < 0) { var num = p; p = q; q = num; } // Compute phi: (p - 1)(q - 1) (Euler's totient function) var p1 = p.subtract(BigInteger.ONE); var q1 = q.subtract(BigInteger.ONE); var phi = p1.multiply(q1); // Ensure e and phi are coprime, ignore this test as OpenSSL does not use it if(phi.gcd(e).compareTo(BigInteger.ONE) !== 0) { console.log ('generateRSAKey() - phi and e are not coprime'); } // Create n, and ensure it has the right number of bits var n = p.multiply(q); if (n.bitLength() !== IONU_RSA_KEY_BITS) { console.log ('generateRSAKey() - invalid bit length ' + n.bitLength()); return undefined; } // set keys var d = e.modInverse(phi); var key = { privateKey: forge.pki.rsa.setPrivateKey (n, e, d, p, q, d.mod(p1), d.mod(q1), q.modInverse(p)), publicKey: forge.pki.rsa.setPublicKey (n, e) }; return key; } };