Sleds/libeye.js/eyedrbg.js

229 lines
6.6 KiB
JavaScript
Raw Normal View History

2025-03-13 21:28:38 +00:00
/**
* @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;
}
};