0
# Random Number Generation
1
2
SJCL provides cryptographically secure pseudo-random number generation with entropy collection from multiple sources, configurable paranoia levels, and both singleton and class-based interfaces for different use cases.
3
4
## Capabilities
5
6
### Global Random Instance
7
8
SJCL provides a pre-configured global random number generator instance with paranoia level 6.
9
10
```javascript { .api }
11
/**
12
* Global PRNG instance with paranoia level 6
13
*/
14
sjcl.random;
15
16
/**
17
* Generate cryptographically secure random words
18
* @param {number} nwords - Number of 32-bit words to generate
19
* @param {number} [paranoia] - Paranoia level override (0-10)
20
* @returns {BitArray} Array of random 32-bit words
21
* @throws {sjcl.exception.notReady} If generator is not sufficiently seeded
22
*/
23
sjcl.random.randomWords(nwords, paranoia);
24
25
/**
26
* Add entropy to the random number generator
27
* @param {number|number[]|string} data - Entropy data
28
* @param {number} estimatedEntropy - Estimated bits of entropy
29
* @param {string} [source] - Source identifier for entropy
30
*/
31
sjcl.random.addEntropy(data, estimatedEntropy, source);
32
33
/**
34
* Check if generator is ready to produce secure random numbers
35
* @param {number} [paranoia] - Paranoia level to check (0-10)
36
* @returns {number} Readiness level (0: not ready, 1: ready, 2: seeded, 3: ready+seeded)
37
*/
38
sjcl.random.isReady(paranoia);
39
40
/**
41
* Get progress toward being ready
42
* @param {number} [paranoia] - Paranoia level to check
43
* @returns {number} Progress value between 0 and 1
44
*/
45
sjcl.random.getProgress(paranoia);
46
```
47
48
**Usage Examples:**
49
50
```javascript
51
const sjcl = require('sjcl');
52
53
// Basic random number generation
54
console.log('Generator ready:', sjcl.random.isReady());
55
56
// Generate random data
57
const randomBytes = sjcl.random.randomWords(4); // 4 * 32 = 128 bits
58
console.log('Random hex:', sjcl.codec.hex.fromBits(randomBytes));
59
60
// Check progress
61
console.log('Progress:', Math.round(sjcl.random.getProgress() * 100) + '%');
62
63
// Add custom entropy
64
sjcl.random.addEntropy(Date.now(), 16, 'timestamp');
65
sjcl.random.addEntropy(Math.random(), 32, 'math-random');
66
67
// Generate different amounts of random data
68
const randomByte = sjcl.random.randomWords(1); // 32 bits
69
const randomKey = sjcl.random.randomWords(8); // 256 bits
70
const randomIV = sjcl.random.randomWords(4); // 128 bits
71
```
72
73
### PRNG Class
74
75
Create custom random number generator instances with specific configurations.
76
77
```javascript { .api }
78
/**
79
* Pseudo-random number generator constructor
80
* @param {number} [defaultParanoia] - Default paranoia level (0-10)
81
*/
82
new sjcl.prng(defaultParanoia);
83
84
/**
85
* Generate random words
86
* @param {number} nwords - Number of 32-bit words to generate
87
* @param {number} [paranoia] - Paranoia level override
88
* @returns {BitArray} Array of random 32-bit words
89
*/
90
sjcl.prng.prototype.randomWords(nwords, paranoia);
91
92
/**
93
* Set default paranoia level
94
* @param {number} paranoia - Paranoia level (0-10)
95
* @param {string} [allowZeroParanoia] - Required string to allow paranoia 0
96
*/
97
sjcl.prng.prototype.setDefaultParanoia(paranoia, allowZeroParanoia);
98
99
/**
100
* Add entropy to the generator
101
* @param {number|number[]|string} data - Entropy data
102
* @param {number} estimatedEntropy - Estimated bits of entropy
103
* @param {string} [source] - Source identifier
104
*/
105
sjcl.prng.prototype.addEntropy(data, estimatedEntropy, source);
106
107
/**
108
* Check generator readiness
109
* @param {number} [paranoia] - Paranoia level to check
110
* @returns {number} Readiness state
111
*/
112
sjcl.prng.prototype.isReady(paranoia);
113
114
/**
115
* Get seeding progress
116
* @param {number} [paranoia] - Paranoia level to check
117
* @returns {number} Progress (0-1)
118
*/
119
sjcl.prng.prototype.getProgress(paranoia);
120
```
121
122
**Usage Examples:**
123
124
```javascript
125
const sjcl = require('sjcl');
126
127
// Create custom PRNG instance
128
const customRNG = new sjcl.prng(8); // High paranoia level
129
130
// Seed the custom RNG
131
customRNG.addEntropy('custom seed data', 64, 'custom');
132
customRNG.addEntropy(Date.now(), 16, 'timestamp');
133
134
// Wait for sufficient entropy
135
while (customRNG.getProgress() < 1.0) {\n customRNG.addEntropy(Math.random(), 32, 'fallback');\n}\n\n// Generate random data\nconst customRandom = customRNG.randomWords(4);\nconsole.log('Custom random:', sjcl.codec.hex.fromBits(customRandom));\n\n// Create low-paranoia RNG for testing\nconst testRNG = new sjcl.prng(0);\ntestRNG.setDefaultParanoia(0, 'Setting paranoia=0 will ruin your security; use it only for testing');\n```\n\n### Entropy Collection\n\nSJCL automatically collects entropy from various browser sources when available.\n\n```javascript { .api }\n/**\n * Start automatic entropy collectors\n */\nsjcl.random.startCollectors();\n\n/**\n * Stop automatic entropy collectors\n */\nsjcl.random.stopCollectors();\n\n/**\n * Add event listener for generator events\n * @param {string} name - Event name ('seeded' or 'progress')\n * @param {Function} callback - Event callback function\n */\nsjcl.random.addEventListener(name, callback);\n\n/**\n * Remove event listener\n * @param {string} name - Event name\n * @param {Function} cb - Callback function to remove\n */\nsjcl.random.removeEventListener(name, cb);\n```\n\n**Usage Examples:**\n\n```javascript\nconst sjcl = require('sjcl');\n\n// Start collecting entropy from browser events\nif (typeof window !== 'undefined') {\n sjcl.random.startCollectors();\n \n // Listen for seeding events\n sjcl.random.addEventListener('seeded', function() {\n console.log('RNG is now seeded and ready!');\n });\n \n sjcl.random.addEventListener('progress', function(progress) {\n console.log('Entropy progress:', Math.round(progress * 100) + '%');\n });\n}\n\n// Manual entropy collection\nfunction collectEntropy() {\n // System time\n sjcl.random.addEntropy(Date.now(), 16, 'time');\n \n // Performance timing (if available)\n if (typeof performance !== 'undefined' && performance.now) {\n sjcl.random.addEntropy(performance.now(), 16, 'performance');\n }\n \n // Math.random as fallback (low entropy)\n sjcl.random.addEntropy(Math.random(), 16, 'math-random');\n \n // User agent string\n if (typeof navigator !== 'undefined') {\n sjcl.random.addEntropy(navigator.userAgent, 8, 'useragent');\n }\n}\n\ncollectEntropy();\n```\n\n## Paranoia Levels\n\nSJCL uses paranoia levels from 0-10 to control entropy requirements:\n\n```javascript\nconst sjcl = require('sjcl');\n\n// Paranoia level meanings:\n// 0: No entropy required (INSECURE - testing only)\n// 1-5: Low to medium entropy requirements\n// 6: Default level (balanced security/performance)\n// 7-10: High entropy requirements (slower but more secure)\n\n// Check requirements for different paranoia levels\nfor (let level = 0; level <= 10; level++) {\n const ready = sjcl.random.isReady(level);\n const progress = sjcl.random.getProgress(level);\n console.log(`Paranoia ${level}: ready=${ready}, progress=${progress.toFixed(2)}`);\n}\n\n// Generate with specific paranoia\nfunction generateSecureRandom(bits, paranoia = 6) {\n const words = Math.ceil(bits / 32);\n \n if (sjcl.random.isReady(paranoia) === 0) {\n throw new sjcl.exception.notReady('Insufficient entropy for paranoia level ' + paranoia);\n }\n \n const random = sjcl.random.randomWords(words, paranoia);\n return sjcl.bitArray.clamp(random, bits);\n}\n\n// Usage\ntry {\n const highSecurityRandom = generateSecureRandom(256, 10);\n const normalRandom = generateSecureRandom(256, 6);\n const fastRandom = generateSecureRandom(256, 2);\n} catch (e) {\n console.error('Not enough entropy:', e.message);\n}\n```\n\n## Cryptographic Key Generation\n\nGenerate cryptographic keys using the secure random number generator:\n\n```javascript\nconst sjcl = require('sjcl');\n\n// AES key generation\nfunction generateAESKey(keySize = 256, paranoia = 6) {\n const words = keySize / 32;\n return sjcl.random.randomWords(words, paranoia);\n}\n\n// Generate keys for different purposes\nfunction generateKeySet() {\n return {\n encryptionKey: generateAESKey(256, 8),\n authKey: generateAESKey(256, 8),\n salt: sjcl.random.randomWords(4, 6), // 128-bit salt\n iv: sjcl.random.randomWords(4, 6), // 128-bit IV\n nonce: sjcl.random.randomWords(3, 6) // 96-bit nonce for GCM\n };\n}\n\n// Secure random password generation\nfunction generateRandomPassword(length = 16) {\n const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*';\n const randomBytes = sjcl.random.randomWords(Math.ceil(length / 4));\n const byteArray = sjcl.codec.bytes.fromBits(randomBytes);\n \n let password = '';\n for (let i = 0; i < length; i++) {\n password += charset[byteArray[i] % charset.length];\n }\n \n return password;\n}\n\n// Usage\nif (sjcl.random.isReady(8)) {\n const keys = generateKeySet();\n const password = generateRandomPassword(20);\n console.log('Generated secure keys and password');\n} else {\n console.log('Waiting for more entropy...');\n}\n```\n\n## Node.js Integration\n\nSJCL automatically uses Node.js crypto.randomBytes when available:\n\n```javascript\nconst sjcl = require('sjcl');\n\n// In Node.js, SJCL automatically uses crypto.randomBytes for seeding\n// This provides high-quality entropy immediately\n\nif (typeof module !== 'undefined' && module.exports) {\n // Running in Node.js\n console.log('Node.js detected - using crypto.randomBytes');\n console.log('Generator ready:', sjcl.random.isReady());\n \n // Can generate secure random numbers immediately\n const nodeRandom = sjcl.random.randomWords(8);\n console.log('Node random:', sjcl.codec.hex.fromBits(nodeRandom));\n} else {\n // Running in browser - may need entropy collection\n console.log('Browser detected - collecting entropy');\n sjcl.random.startCollectors();\n}\n```\n\n## Advanced Usage Patterns\n\n### Deterministic Random for Testing\n\n```javascript\nconst sjcl = require('sjcl');\n\n// Create deterministic RNG for reproducible tests\nfunction createTestRNG(seed) {\n const testRNG = new sjcl.prng(0);\n testRNG.setDefaultParanoia(0, 'Setting paranoia=0 will ruin your security; use it only for testing');\n \n // Seed with deterministic value\n const seedBits = sjcl.codec.utf8String.toBits(seed);\n testRNG.addEntropy(seedBits, 256, 'test-seed');\n \n return testRNG;\n}\n\n// Usage in tests\nconst testRNG = createTestRNG('test-seed-123');\nconst testRandom1 = testRNG.randomWords(4);\nconst testRandom2 = testRNG.randomWords(4);\n\n// Same seed will produce same sequence\nconst testRNG2 = createTestRNG('test-seed-123');\nconst testRandom1Copy = testRNG2.randomWords(4);\nconsole.log('Reproducible:', sjcl.bitArray.equal(testRandom1, testRandom1Copy));\n```\n\n### Entropy Pool Monitoring\n\n```javascript\nconst sjcl = require('sjcl');\n\nclass EntropyMonitor {\n constructor() {\n this.callbacks = [];\n this.monitoring = false;\n }\n \n start() {\n if (this.monitoring) return;\n \n this.monitoring = true;\n sjcl.random.startCollectors();\n \n const checkProgress = () => {\n const progress = sjcl.random.getProgress();\n const ready = sjcl.random.isReady();\n \n this.callbacks.forEach(cb => cb({ progress, ready }));\n \n if (this.monitoring) {\n setTimeout(checkProgress, 1000);\n }\n };\n \n checkProgress();\n }\n \n stop() {\n this.monitoring = false;\n sjcl.random.stopCollectors();\n }\n \n onUpdate(callback) {\n this.callbacks.push(callback);\n }\n}\n\n// Usage\nconst monitor = new EntropyMonitor();\nmonitor.onUpdate(({ progress, ready }) => {\n console.log(`Entropy: ${Math.round(progress * 100)}%, Ready: ${ready}`);\n});\n\nmonitor.start();\n```\n\n## Security Recommendations\n\n1. **Seeding**: Always ensure the generator is properly seeded before use\n2. **Paranoia Levels**: Use higher paranoia levels (6-8) for cryptographic keys\n3. **Entropy Sources**: Combine multiple entropy sources when possible\n4. **Testing**: Use paranoia 0 ONLY for testing, never in production\n5. **Monitoring**: Monitor entropy collection in browser environments\n6. **Node.js**: Leverage crypto.randomBytes in Node.js for better entropy\n\n## Common Pitfalls\n\n1. **Insufficient Entropy**: Using generator before it's properly seeded\n2. **Low Paranoia**: Using paranoia 0 in production\n3. **Predictable Seeds**: Seeding with predictable values\n4. **Entropy Estimation**: Over-estimating entropy from weak sources\n5. **Timing**: Not waiting for sufficient entropy collection in browsers