1 | /**
|
---|
2 | * An API for getting cryptographically-secure random bytes. The bytes are
|
---|
3 | * generated using the Fortuna algorithm devised by Bruce Schneier and
|
---|
4 | * Niels Ferguson.
|
---|
5 | *
|
---|
6 | * Getting strong random bytes is not yet easy to do in javascript. The only
|
---|
7 | * truish random entropy that can be collected is from the mouse, keyboard, or
|
---|
8 | * from timing with respect to page loads, etc. This generator makes a poor
|
---|
9 | * attempt at providing random bytes when those sources haven't yet provided
|
---|
10 | * enough entropy to initially seed or to reseed the PRNG.
|
---|
11 | *
|
---|
12 | * @author Dave Longley
|
---|
13 | *
|
---|
14 | * Copyright (c) 2009-2014 Digital Bazaar, Inc.
|
---|
15 | */
|
---|
16 | var forge = require('./forge');
|
---|
17 | require('./aes');
|
---|
18 | require('./sha256');
|
---|
19 | require('./prng');
|
---|
20 | require('./util');
|
---|
21 |
|
---|
22 | (function() {
|
---|
23 |
|
---|
24 | // forge.random already defined
|
---|
25 | if(forge.random && forge.random.getBytes) {
|
---|
26 | module.exports = forge.random;
|
---|
27 | return;
|
---|
28 | }
|
---|
29 |
|
---|
30 | (function(jQuery) {
|
---|
31 |
|
---|
32 | // the default prng plugin, uses AES-128
|
---|
33 | var prng_aes = {};
|
---|
34 | var _prng_aes_output = new Array(4);
|
---|
35 | var _prng_aes_buffer = forge.util.createBuffer();
|
---|
36 | prng_aes.formatKey = function(key) {
|
---|
37 | // convert the key into 32-bit integers
|
---|
38 | var tmp = forge.util.createBuffer(key);
|
---|
39 | key = new Array(4);
|
---|
40 | key[0] = tmp.getInt32();
|
---|
41 | key[1] = tmp.getInt32();
|
---|
42 | key[2] = tmp.getInt32();
|
---|
43 | key[3] = tmp.getInt32();
|
---|
44 |
|
---|
45 | // return the expanded key
|
---|
46 | return forge.aes._expandKey(key, false);
|
---|
47 | };
|
---|
48 | prng_aes.formatSeed = function(seed) {
|
---|
49 | // convert seed into 32-bit integers
|
---|
50 | var tmp = forge.util.createBuffer(seed);
|
---|
51 | seed = new Array(4);
|
---|
52 | seed[0] = tmp.getInt32();
|
---|
53 | seed[1] = tmp.getInt32();
|
---|
54 | seed[2] = tmp.getInt32();
|
---|
55 | seed[3] = tmp.getInt32();
|
---|
56 | return seed;
|
---|
57 | };
|
---|
58 | prng_aes.cipher = function(key, seed) {
|
---|
59 | forge.aes._updateBlock(key, seed, _prng_aes_output, false);
|
---|
60 | _prng_aes_buffer.putInt32(_prng_aes_output[0]);
|
---|
61 | _prng_aes_buffer.putInt32(_prng_aes_output[1]);
|
---|
62 | _prng_aes_buffer.putInt32(_prng_aes_output[2]);
|
---|
63 | _prng_aes_buffer.putInt32(_prng_aes_output[3]);
|
---|
64 | return _prng_aes_buffer.getBytes();
|
---|
65 | };
|
---|
66 | prng_aes.increment = function(seed) {
|
---|
67 | // FIXME: do we care about carry or signed issues?
|
---|
68 | ++seed[3];
|
---|
69 | return seed;
|
---|
70 | };
|
---|
71 | prng_aes.md = forge.md.sha256;
|
---|
72 |
|
---|
73 | /**
|
---|
74 | * Creates a new PRNG.
|
---|
75 | */
|
---|
76 | function spawnPrng() {
|
---|
77 | var ctx = forge.prng.create(prng_aes);
|
---|
78 |
|
---|
79 | /**
|
---|
80 | * Gets random bytes. If a native secure crypto API is unavailable, this
|
---|
81 | * method tries to make the bytes more unpredictable by drawing from data that
|
---|
82 | * can be collected from the user of the browser, eg: mouse movement.
|
---|
83 | *
|
---|
84 | * If a callback is given, this method will be called asynchronously.
|
---|
85 | *
|
---|
86 | * @param count the number of random bytes to get.
|
---|
87 | * @param [callback(err, bytes)] called once the operation completes.
|
---|
88 | *
|
---|
89 | * @return the random bytes in a string.
|
---|
90 | */
|
---|
91 | ctx.getBytes = function(count, callback) {
|
---|
92 | return ctx.generate(count, callback);
|
---|
93 | };
|
---|
94 |
|
---|
95 | /**
|
---|
96 | * Gets random bytes asynchronously. If a native secure crypto API is
|
---|
97 | * unavailable, this method tries to make the bytes more unpredictable by
|
---|
98 | * drawing from data that can be collected from the user of the browser,
|
---|
99 | * eg: mouse movement.
|
---|
100 | *
|
---|
101 | * @param count the number of random bytes to get.
|
---|
102 | *
|
---|
103 | * @return the random bytes in a string.
|
---|
104 | */
|
---|
105 | ctx.getBytesSync = function(count) {
|
---|
106 | return ctx.generate(count);
|
---|
107 | };
|
---|
108 |
|
---|
109 | return ctx;
|
---|
110 | }
|
---|
111 |
|
---|
112 | // create default prng context
|
---|
113 | var _ctx = spawnPrng();
|
---|
114 |
|
---|
115 | // add other sources of entropy only if window.crypto.getRandomValues is not
|
---|
116 | // available -- otherwise this source will be automatically used by the prng
|
---|
117 | var getRandomValues = null;
|
---|
118 | var globalScope = forge.util.globalScope;
|
---|
119 | var _crypto = globalScope.crypto || globalScope.msCrypto;
|
---|
120 | if(_crypto && _crypto.getRandomValues) {
|
---|
121 | getRandomValues = function(arr) {
|
---|
122 | return _crypto.getRandomValues(arr);
|
---|
123 | };
|
---|
124 | }
|
---|
125 |
|
---|
126 | if(forge.options.usePureJavaScript ||
|
---|
127 | (!forge.util.isNodejs && !getRandomValues)) {
|
---|
128 | // if this is a web worker, do not use weak entropy, instead register to
|
---|
129 | // receive strong entropy asynchronously from the main thread
|
---|
130 | if(typeof window === 'undefined' || window.document === undefined) {
|
---|
131 | // FIXME:
|
---|
132 | }
|
---|
133 |
|
---|
134 | // get load time entropy
|
---|
135 | _ctx.collectInt(+new Date(), 32);
|
---|
136 |
|
---|
137 | // add some entropy from navigator object
|
---|
138 | if(typeof(navigator) !== 'undefined') {
|
---|
139 | var _navBytes = '';
|
---|
140 | for(var key in navigator) {
|
---|
141 | try {
|
---|
142 | if(typeof(navigator[key]) == 'string') {
|
---|
143 | _navBytes += navigator[key];
|
---|
144 | }
|
---|
145 | } catch(e) {
|
---|
146 | /* Some navigator keys might not be accessible, e.g. the geolocation
|
---|
147 | attribute throws an exception if touched in Mozilla chrome://
|
---|
148 | context.
|
---|
149 |
|
---|
150 | Silently ignore this and just don't use this as a source of
|
---|
151 | entropy. */
|
---|
152 | }
|
---|
153 | }
|
---|
154 | _ctx.collect(_navBytes);
|
---|
155 | _navBytes = null;
|
---|
156 | }
|
---|
157 |
|
---|
158 | // add mouse and keyboard collectors if jquery is available
|
---|
159 | if(jQuery) {
|
---|
160 | // set up mouse entropy capture
|
---|
161 | jQuery().mousemove(function(e) {
|
---|
162 | // add mouse coords
|
---|
163 | _ctx.collectInt(e.clientX, 16);
|
---|
164 | _ctx.collectInt(e.clientY, 16);
|
---|
165 | });
|
---|
166 |
|
---|
167 | // set up keyboard entropy capture
|
---|
168 | jQuery().keypress(function(e) {
|
---|
169 | _ctx.collectInt(e.charCode, 8);
|
---|
170 | });
|
---|
171 | }
|
---|
172 | }
|
---|
173 |
|
---|
174 | /* Random API */
|
---|
175 | if(!forge.random) {
|
---|
176 | forge.random = _ctx;
|
---|
177 | } else {
|
---|
178 | // extend forge.random with _ctx
|
---|
179 | for(var key in _ctx) {
|
---|
180 | forge.random[key] = _ctx[key];
|
---|
181 | }
|
---|
182 | }
|
---|
183 |
|
---|
184 | // expose spawn PRNG
|
---|
185 | forge.random.createInstance = spawnPrng;
|
---|
186 |
|
---|
187 | module.exports = forge.random;
|
---|
188 |
|
---|
189 | })(typeof(jQuery) !== 'undefined' ? jQuery : null);
|
---|
190 |
|
---|
191 | })();
|
---|