1/** 2 * A javascript implementation of a cryptographically-secure 3 * Pseudo Random Number Generator (PRNG). The Fortuna algorithm is followed 4 * here though the use of SHA-256 is not enforced; when generating an 5 * a PRNG context, the hashing algorithm and block cipher used for 6 * the generator are specified via a plugin. 7 * 8 * @author Dave Longley 9 * 10 * Copyright (c) 2010-2014 Digital Bazaar, Inc. 11 */ 12(function() { 13/* ########## Begin module implementation ########## */ 14function initModule(forge) { 15 16var _nodejs = ( 17 typeof process !== 'undefined' && process.versions && process.versions.node); 18var _crypto = null; 19if(!forge.disableNativeCode && _nodejs && !process.versions['node-webkit']) { 20 _crypto = require('crypto'); 21} 22 23/* PRNG API */ 24var prng = forge.prng = forge.prng || {}; 25 26/** 27 * Creates a new PRNG context. 28 * 29 * A PRNG plugin must be passed in that will provide: 30 * 31 * 1. A function that initializes the key and seed of a PRNG context. It 32 * will be given a 16 byte key and a 16 byte seed. Any key expansion 33 * or transformation of the seed from a byte string into an array of 34 * integers (or similar) should be performed. 35 * 2. The cryptographic function used by the generator. It takes a key and 36 * a seed. 37 * 3. A seed increment function. It takes the seed and returns seed + 1. 38 * 4. An api to create a message digest. 39 * 40 * For an example, see random.js. 41 * 42 * @param plugin the PRNG plugin to use. 43 */ 44prng.create = function(plugin) { 45 var ctx = { 46 plugin: plugin, 47 key: null, 48 seed: null, 49 time: null, 50 // number of reseeds so far 51 reseeds: 0, 52 // amount of data generated so far 53 generated: 0 54 }; 55 56 // create 32 entropy pools (each is a message digest) 57 var md = plugin.md; 58 var pools = new Array(32); 59 for(var i = 0; i < 32; ++i) { 60 pools[i] = md.create(); 61 } 62 ctx.pools = pools; 63 64 // entropy pools are written to cyclically, starting at index 0 65 ctx.pool = 0; 66 67 /** 68 * Generates random bytes. The bytes may be generated synchronously or 69 * asynchronously. Web workers must use the asynchronous interface or 70 * else the behavior is undefined. 71 * 72 * @param count the number of random bytes to generate. 73 * @param [callback(err, bytes)] called once the operation completes. 74 * 75 * @return count random bytes as a string. 76 */ 77 ctx.generate = function(count, callback) { 78 // do synchronously 79 if(!callback) { 80 return ctx.generateSync(count); 81 } 82 83 // simple generator using counter-based CBC 84 var cipher = ctx.plugin.cipher; 85 var increment = ctx.plugin.increment; 86 var formatKey = ctx.plugin.formatKey; 87 var formatSeed = ctx.plugin.formatSeed; 88 var b = forge.util.createBuffer(); 89 90 // reset key for every request 91 ctx.key = null; 92 93 generate(); 94 95 function generate(err) { 96 if(err) { 97 return callback(err); 98 } 99 100 // sufficient bytes generated 101 if(b.length() >= count) { 102 return callback(null, b.getBytes(count)); 103 } 104 105 // if amount of data generated is greater than 1 MiB, trigger reseed 106 if(ctx.generated > 0xfffff) { 107 ctx.key = null; 108 } 109 110 if(ctx.key === null) { 111 // prevent stack overflow 112 return forge.util.nextTick(function() { 113 _reseed(generate); 114 }); 115 } 116 117 // generate the random bytes 118 var bytes = cipher(ctx.key, ctx.seed); 119 ctx.generated += bytes.length; 120 b.putBytes(bytes); 121 122 // generate bytes for a new key and seed 123 ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed))); 124 ctx.seed = formatSeed(cipher(ctx.key, ctx.seed)); 125 126 forge.util.setImmediate(generate); 127 } 128 }; 129 130 /** 131 * Generates random bytes synchronously. 132 * 133 * @param count the number of random bytes to generate. 134 * 135 * @return count random bytes as a string. 136 */ 137 ctx.generateSync = function(count) { 138 // simple generator using counter-based CBC 139 var cipher = ctx.plugin.cipher; 140 var increment = ctx.plugin.increment; 141 var formatKey = ctx.plugin.formatKey; 142 var formatSeed = ctx.plugin.formatSeed; 143 144 // reset key for every request 145 ctx.key = null; 146 147 var b = forge.util.createBuffer(); 148 while(b.length() < count) { 149 // if amount of data generated is greater than 1 MiB, trigger reseed 150 if(ctx.generated > 0xfffff) { 151 ctx.key = null; 152 } 153 154 if(ctx.key === null) { 155 _reseedSync(); 156 } 157 158 // generate the random bytes 159 var bytes = cipher(ctx.key, ctx.seed); 160 ctx.generated += bytes.length; 161 b.putBytes(bytes); 162 163 // generate bytes for a new key and seed 164 ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed))); 165 ctx.seed = formatSeed(cipher(ctx.key, ctx.seed)); 166 } 167 168 return b.getBytes(count); 169 }; 170 171 /** 172 * Private function that asynchronously reseeds a generator. 173 * 174 * @param callback(err) called once the operation completes. 175 */ 176 function _reseed(callback) { 177 if(ctx.pools[0].messageLength >= 32) { 178 _seed(); 179 return callback(); 180 } 181 // not enough seed data... 182 var needed = (32 - ctx.pools[0].messageLength) << 5; 183 ctx.seedFile(needed, function(err, bytes) { 184 if(err) { 185 return callback(err); 186 } 187 ctx.collect(bytes); 188 _seed(); 189 callback(); 190 }); 191 } 192 193 /** 194 * Private function that synchronously reseeds a generator. 195 */ 196 function _reseedSync() { 197 if(ctx.pools[0].messageLength >= 32) { 198 return _seed(); 199 } 200 // not enough seed data... 201 var needed = (32 - ctx.pools[0].messageLength) << 5; 202 ctx.collect(ctx.seedFileSync(needed)); 203 _seed(); 204 } 205 206 /** 207 * Private function that seeds a generator once enough bytes are available. 208 */ 209 function _seed() { 210 // create a plugin-based message digest 211 var md = ctx.plugin.md.create(); 212
213 // digest pool 0's entropy and restart it 214 md.update(ctx.pools[0].digest().getBytes()); 215 ctx.pools[0].start(); 216 217 // digest the entropy of other pools whose index k meet the 218 // condition '2^k mod n == 0' where n is the number of reseeds 219 var k = 1; 220 for(var i = 1; i < 32; ++i) { 221 // prevent signed numbers from being used 222 k = (k === 31) ? 0x80000000 : (k << 2); 223 if(k % ctx.reseeds === 0) { 224 md.update(ctx.pools[i].digest().getBytes()); 225 ctx.pools[i].start(); 226 } 227 } 228 229 // get digest for key bytes and iterate again for seed bytes 230 var keyBytes = md.digest().getBytes(); 231 md.start(); 232 md.update(keyBytes); 233 var seedBytes = md.digest().getBytes(); 234 235 // update 236 ctx.key = ctx.plugin.formatKey(keyBytes); 237 ctx.seed = ctx.plugin.formatSeed(seedBytes); 238 ctx.reseeds = (ctx.reseeds === 0xffffffff) ? 0 : ctx.reseeds + 1; 239 ctx.generated = 0; 240 } 241 242 /** 243 * The built-in default seedFile. This seedFile is used when entropy 244 * is needed immediately. 245 * 246 * @param needed the number of bytes that are needed. 247 * 248 * @return the random bytes. 249 */ 250 function defaultSeedFile(needed) { 251 // use window.crypto.getRandomValues strong source of entropy if available 252 var getRandomValues = null; 253 if(typeof window !== 'undefined') { 254 var _crypto = window.crypto || window.msCrypto; 255 if(_crypto && _crypto.getRandomValues) { 256 getRandomValues = function(arr) { 257 return _crypto.getRandomValues(arr); 258 }; 259 } 260 } 261 262 var b = forge.util.createBuffer(); 263 if(getRandomValues) { 264 while(b.length() < needed) { 265 // max byte length is 65536 before QuotaExceededError is thrown 266 // http://www.w3.org/TR/WebCryptoAPI/#RandomSource-method-getRandomValues 267 var count = Math.max(1, Math.min(needed - b.length(), 65536) / 4); 268 var entropy = new Uint32Array(Math.floor(count)); 269 try { 270 getRandomValues(entropy); 271 for(var i = 0; i < entropy.length; ++i) { 272 b.putInt32(entropy[i]); 273 } 274 } catch(e) { 275 /* only ignore QuotaExceededError */ 276 if(!(typeof QuotaExceededError !== 'undefined' && 277 e instanceof QuotaExceededError)) { 278 throw e; 279 } 280 } 281 } 282 } 283 284 // be sad and add some weak random data 285 if(b.length() < needed) { 286 /* Draws from Park-Miller "minimal standard" 31 bit PRNG, 287 implemented with David G. Carta's optimization: with 32 bit math 288 and without division (Public Domain). */ 289 var hi, lo, next; 290 var seed = Math.floor(Math.random() * 0x010000); 291 while(b.length() < needed) { 292 lo = 16807 * (seed & 0xFFFF); 293 hi = 16807 * (seed >> 16); 294 lo += (hi & 0x7FFF) << 16; 295 lo += hi >> 15; 296 lo = (lo & 0x7FFFFFFF) + (lo >> 31); 297 seed = lo & 0xFFFFFFFF; 298 299 // consume lower 3 bytes of seed 300 for(var i = 0; i < 3; ++i) { 301 // throw in more pseudo random 302 next = seed >>> (i << 3); 303 next ^= Math.floor(Math.random() * 0x0100); 304 b.putByte(String.fromCharCode(next & 0xFF)); 305 } 306 } 307 } 308 309 return b.getBytes(needed); 310 } 311 // initialize seed file APIs 312 if(_crypto) { 313 // use nodejs async API 314 ctx.seedFile = function(needed, callback) { 315 _crypto.randomBytes(needed, function(err, bytes) { 316 if(err) { 317 return callback(err); 318 } 319 callback(null, bytes.toString()); 320 }); 321 }; 322 // use nodejs sync API 323 ctx.seedFileSync = function(needed) { 324 return _crypto.randomBytes(needed).toString(); 325 }; 326 } else { 327 ctx.seedFile = function(needed, callback) { 328 try { 329 callback(null, defaultSeedFile(needed)); 330 } catch(e) { 331 callback(e); 332 } 333 }; 334 ctx.seedFileSync = defaultSeedFile; 335 } 336 337 /** 338 * Adds entropy to a prng ctx's accumulator. 339 * 340 * @param bytes the bytes of entropy as a string. 341 */ 342 ctx.collect = function(bytes) { 343 // iterate over pools distributing entropy cyclically 344 var count = bytes.length; 345 for(var i = 0; i < count; ++i) { 346 ctx.pools[ctx.pool].update(bytes.substr(i, 1)); 347 ctx.pool = (ctx.pool === 31) ? 0 : ctx.pool + 1; 348 } 349 }; 350 351 /** 352 * Collects an integer of n bits. 353 * 354 * @param i the integer entropy. 355 * @param n the number of bits in the integer. 356 */ 357 ctx.collectInt = function(i, n) { 358 var bytes = ''; 359 for(var x = 0; x < n; x += 8) { 360 bytes += String.fromCharCode((i >> x) & 0xFF); 361 } 362 ctx.collect(bytes); 363 }; 364 365 /** 366 * Registers a Web Worker to receive immediate entropy from the main thread. 367 * This method is required until Web Workers can access the native crypto 368 * API. This method should be called twice for each created worker, once in 369 * the main thread, and once in the worker itself. 370 * 371 * @param worker the worker to register. 372 */ 373 ctx.registerWorker = function(worker) { 374 // worker receives random bytes 375 if(worker === self) { 376 ctx.seedFile = function(needed, callback) { 377 function listener(e) { 378 var data = e.data; 379 if(data.forge && data.forge.prng) { 380 self.removeEventListener('message', listener); 381 callback(data.forge.prng.err, data.forge.prng.bytes); 382 } 383 } 384 self.addEventListener('message', listener); 385 self.postMessage({forge: {prng: {needed: needed}}}); 386 }; 387 } else { 388 // main thread sends random bytes upon request 389 var listener = function(e) { 390 var data = e.data; 391 if(data.forge && data.forge.prng) { 392 ctx.seedFile(data.forge.prng.needed, function(err, bytes) { 393 worker.postMessage({forge: {prng: {err: err, bytes: bytes}}}); 394 }); 395 } 396 }; 397 // TODO: do we need to remove the event listener when the worker dies? 398 worker.addEventListener('message', listener); 399 } 400 }; 401 402 return ctx; 403}; 404 405} // end module implementation 406 407/* ########## Begin module wrapper ########## */ 408var name = 'prng'; 409if(typeof define !== 'function') { 410 // NodeJS -> AMD 411 if(typeof module === 'object' && module.exports) { 412 var nodeJS = true; 413 define = function(ids, factory) { 414 factory(require, module); 415 }; 416 } else { 417 // <script> 418 if(typeof forge === 'undefined') { 419 forge = {}; 420 } 421 return initModule(forge); 422 } 423} 424// AMD 425var deps;
426var defineFunc = function(require, module) { 427 module.exports = function(forge) { 428 var mods = deps.map(function(dep) { 429 return require(dep); 430 }).concat(initModule); 431 // handle circular dependencies 432 forge = forge || {}; 433 forge.defined = forge.defined || {}; 434 if(forge.defined[name]) { 435 return forge[name]; 436 } 437 forge.defined[name] = true; 438 for(var i = 0; i < mods.length; ++i) { 439 mods[i](forge); 440 } 441 return forge[name]; 442 }; 443}; 444var tmpDefine = define; 445define = function(ids, factory) { 446 deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2); 447 if(nodeJS) { 448 delete define; 449 return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0)); 450 } 451 define = tmpDefine; 452 return define.apply(null, Array.prototype.slice.call(arguments, 0)); 453}; 454define(['require', 'module', './md', './util'], function() { 455 defineFunc.apply(null, Array.prototype.slice.call(arguments, 0)); 456}); 457 458})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.