@h0llyw00dzz/crypto-rand
v0.1.5
Published
Cryptographically secure random utilities for Node.js and browsers
Maintainers
Readme
crypto-rand
Cryptographically secure random utilities for Node.js and browsers.
[!IMPORTANT]
FIPS Compliance Disclaimer
This package may not conform to FIPS (Federal Information Processing Standards) requirements for cryptographic modules. Organizations requiring FIPS-validated cryptography should conduct their own compliance assessment before deployment.
Key Considerations:
Platform Dependencies: Cryptographic security relies on underlying platform implementations (Node.js
cryptomodule, Web Crypto API) which may not be FIPS-certified across all deployment environmentsAlgorithm Implementation: Certain methods, particularly
randPrime/randPrimeAsync, utilize probabilistic algorithms (Miller-Rabin primality testing) that may not align with FIPS-approved deterministic validation proceduresCross-Platform Design: Compatibility requirements across Node.js and browser environments necessitate implementation choices that may not satisfy strict FIPS compliance criteria
Validation Status: This library has not undergone formal FIPS certification or validation testing
Security Assurance: Non-compliance with FIPS standards does not indicate cryptographic weakness. This library employs industry-standard secure random number generation and is suitable for general-purpose cryptographic applications where FIPS certification is not mandated.
For FIPS-compliant environments, consult your organization's security policies and consider using FIPS-validated cryptographic modules.
Installation
[!NOTE]
The minimum required Node.js version is 19.0.0. It also works well with the latest Node.js versions, as it doesn't rely on external dependencies. It primarily uses the Node.js
cryptomodule, which leverages OpenSSL or other cryptographic libraries. For example, on Unix-like systems, Node.js might use/dev/urandomor/dev/random.
[!TIP]
Since it primarily uses the Node.js
cryptomodule, which leverages OpenSSL or other cryptographic libraries, it's recommended to customize the source of randomness. The default source might have poor entropy. For example, in Node.js, you can customize it through the OpenSSL configuration.
npm install @h0llyw00dzz/crypto-randTroubleshooting: If the standard import doesn't work after installation for an older version, try using the direct path:
import { Crypto, randString, randInt } from '@h0llyw00dzz/crypto-rand/dist/src/crypto_rand';
Usage
import { Crypto, randString, randInt } from '@h0llyw00dzz/crypto-rand';
// Generate secure random number between 0 and 1
const randomFloat = Crypto.rand();
// Generate secure random integer
const randomNumber = randInt(1, 100);
// Generate secure random string
const token = randString(32);
// Generate secure password
const password = Crypto.randPassword({ length: 16 });
// Generate UUID
const id = Crypto.randUUID();
// Using async methods
async function generateRandomValues() {
// Generate secure random number between 0 and 1 asynchronously
const randomFloatAsync = await randAsync();
// Generate secure random bytes asynchronously
const randomBytes = await randBytesAsync(16);
// Generate secure random hex string asynchronously
const randomHex = await Crypto.randHexAsync(32);
console.log({ randomFloatAsync, randomBytes, randomHex });
}Features
- Cryptographically secure random number generation
- Drop-in replacement for Math.random()
- Random string generation with custom charsets
- Array shuffling and random selection
- UUID generation
- Weighted random choices
- Normal distribution random numbers
- Cross-platform compatibility (Node.js + Browser)
- Asynchronous methods for non-blocking operations
- Written in TypeScript for static typing (well-known) and better maintainability
- And much more!
TODO - Features
- [x] ~~Breaking changes: Update target and lib to ES2020 in
tsconfig.jsonor later to implement additional random methods such asrandPrime~~ (Implemented in current version) - [ ] Add more post-quantum cryptography methods
- [ ] Implement a source for a CSPRNG/CPRNG using TPM 2.0 with C/C++ addons. This is feasible, as the maximum output is likely 32 bytes, which is typical for TPM 2.0. Tested on ROG STRIX B450-F GAMING II, the maximum is 32 bytes.
API Documentation
Static Methods
Crypto.rand()- Secure replacement for Math.random()Crypto.randInt(min, max)- Generate random integer in rangeCrypto.randN(max)- Generate random integer 0 to max-1Crypto.randString(length, charset?)- Generate random stringCrypto.randBool(probability?)- Generate random booleanCrypto.randChoice(array)- Pick random array elementCrypto.shuffle(array)- Shuffle array securelyCrypto.randHex(length)- Generate random hex stringCrypto.randBase64(length)- Generate random base64 stringCrypto.randFloat(min, max)- Generate random float in rangeCrypto.randWeighted(items, weights)- Weighted random selectionCrypto.randNormal(mean, stdDev)- Normal distribution randomCrypto.randSeed()- Generate cryptographically secure seedCrypto.randUUID()- Generate UUID v4Crypto.randGaussian(mean, stdDev)- Similar to randNormal, but with different handling of edge casesCrypto.randSubset(array, size)- Select a random subset from an arrayCrypto.randWalk(steps, stepSize?)- Generate random walk sequence (stepSize defaults to 1)Crypto.randPassword(options)- Generate secure password with configurable requirementsCrypto.randLattice(dimension?, modulus?)- Generate lattice-based cryptographically secure random numberCrypto.randPrime(bits?, iterations?, enhanced?)- Generate cryptographically secure random prime number with optional FIPS-enhanced modeCrypto.randBigInt(bits?)- Generate cryptographically secure random bigint with specified bit lengthCrypto.randExponential(lambda?)- Generate random number with exponential distribution
Static Async Methods
Crypto.randAsync()- Async version of rand()Crypto.randBytesAsync(size)- Async version of randBytes()Crypto.randHexAsync(length)- Async version of randHex()Crypto.randBase64Async(length)- Async version of randBase64()Crypto.randSeedAsync()- Async version of randSeed()Crypto.randVersionAsync()- Async version of randVersion()Crypto.randPrimeAsync(bits?, iterations?, enhanced?)- Async version of randPrime() with optional FIPS-enhanced modeCrypto.randBigIntAsync(bits?)- Async version of randBigInt()
[!TIP] Benefits of Async Methods
Async methods provide several advantages when generating cryptographically secure random values:
- Non-blocking operation: Async methods don't block the main thread, improving application responsiveness
- Concurrent execution: Multiple async operations can run simultaneously with
Promise.all(). However, this doesn't guarantee true parallelism. Read more here.- Better performance: For large or frequent random number generation, async methods can offer better throughput
- Modern JavaScript patterns: Works well with async/await syntax for cleaner code
- Improved RSA operations: Using
randPrimeAsyncandrandBigIntAsyncallows for non-blocking prime generation for RSA key pairsUse async methods when generating large amounts of random data or when you need to maintain UI responsiveness in applications.
Example: Async RSA Key Generation
import { randPrimeAsync } from '@h0llyw00dzz/crypto-rand'; import { modInverse } from '@h0llyw00dzz/crypto-rand'; // If available, or implement your own async function generateRSAKeyPair(bitLength = 1024) { console.log(`Generating ${2 * bitLength}-bit RSA key pair asynchronously...`); // Generate two primes concurrently let p, q, n, phi; // Loop to ensure modulus n is of the expected bit length do { // Using enhanced FIPS mode for stronger primality testing [p, q] = await Promise.all([ randPrimeAsync(bitLength, 10, true), // true enables FIPS-enhanced mode randPrimeAsync(bitLength, 10, true) ]); n = p * q; // modulus phi = (p - 1n) * (q - 1n); // Euler's totient } while (n.toString(2).length !== 2 * bitLength); const e = 65537n; // Common public exponent const d = modInverse(e, phi); // Private exponent return { publicKey: { e, n }, privateKey: { d, n, p, q } }; }
[!NOTE]
randNormal vs. randGaussian: Both methods generate normally distributed random numbers using the Box-Muller transform.
randNormalensures the logarithm function never receives zero by adjusting its input range, whilerandGaussianuses a direct approach.randSubset: Allows selection of a random subset from an array, useful for sampling without replacement.
randWalk: Generates a sequence representing a random walk starting from position 0, where each step moves by ±stepSize. Returns an array containing all positions including the starting position.
randPassword vs. randString:
randPasswordis specifically designed for password generation with built-in character type controls, password-specific features like excluding similar-looking characters (0O1lI), and ensuring proper character distribution for strong passwords. WhilerandStringis a general-purpose string generator,randPasswordis optimized for creating secure passwords with common password policy requirements.randLattice: Generates cryptographically secure random numbers using lattice-based mathematical operations and the Learning With Errors (LWE) problem. Uses high-dimensional vector operations with Gaussian error distribution for enhanced security.
randPrime: Generates cryptographically secure random prime numbers of specified bit length using the Miller-Rabin primality test. This is useful for cryptographic applications like RSA key generation that require large prime numbers. The function now supports an enhanced FIPS mode that implements additional checks following the FIPS 186-5 standard, including GCD verification between random witnesses and the tested number.
randBigInt: Generates cryptographically secure random bigints with exactly the specified bit length. It ensures the most significant bit is set to 1 (to maintain the exact bit length) and the least significant bit is set to 1 (making it odd). This method is useful for cryptographic operations that require large random integers, and is used internally by
randPrime.randExponential: Generates random numbers following an exponential distribution with rate parameter
lambda(default: 1). The exponential distribution is commonly used for modeling time between independent events that occur at a constant average rate, such as arrival times, failure times, or waiting times in queuing theory. The mean of the distribution is 1/λ and the variance is 1/λ².
Character Sets
import {
DEFAULT_CHARSET,
HEX_CHARSET,
ALPHANUMERIC_CHARSET,
NUMERIC_CHARSET,
SPECIAL_CHARSET
} from '@h0llyw00dzz/crypto-rand';
// Use custom charset
const customToken = randString(16, ALPHANUMERIC_CHARSET);Advanced Examples
// Generate secure password
const password = Crypto.randString(16, FULL_CHARSET);
// Shuffle an array
const shuffled = Crypto.shuffle([1, 2, 3, 4, 5]);
// Weighted random choice
const items = ['apple', 'banana', 'orange'];
const weights = [0.5, 0.3, 0.2];
const choice = Crypto.randWeighted(items, weights);
// Normal distribution random number
const normalRandom = Crypto.randNormal(0, 1); // mean=0, stdDev=1
// Generate random bytes
const bytes = Crypto.randBytes(32);randPrime: RSA key pair simulation example
import { Crypto } from '@h0llyw00dzz/crypto-rand';
// RSA key pair simulation example
console.log('🔐 RSA Key Pair Simulation');
console.log('Generating two 512-bit primes for RSA...');
console.time('RSA prime pair generation');
// Using enhanced FIPS mode for stronger primality testing
const p: bigint = Crypto.randPrime(512, 10, true); // true enables FIPS-enhanced mode
const q: bigint = Crypto.randPrime(512, 10, true);
console.timeEnd('RSA prime pair generation');
const n: bigint = p * q;
const phi: bigint = (p - 1n) * (q - 1n);
console.log('\n📊 Full RSA Key Parameters:');
console.log('━'.repeat(80));
console.log(`\n🔢 Prime p (${p.toString(2).length} bits):`);
console.log(p.toString());
console.log(`\n🔢 Prime q (${q.toString(2).length} bits):`);
console.log(q.toString());
console.log(`\n🔐 Modulus n = p × q (${n.toString(2).length} bits):`);
console.log(n.toString());
console.log(`\n📈 Euler's totient φ(n) = (p-1)(q-1) (${phi.toString().length} digits):`);
console.log(phi.toString());
console.log('\n🎯 RSA Key Analysis:');
console.log('━'.repeat(40));
console.log(`Prime p bit length: ${p.toString(2).length} bits`);
console.log(`Prime q bit length: ${q.toString(2).length} bits`);
console.log(`Modulus n bit length: ${n.toString(2).length} bits`);
console.log(`Modulus n decimal length: ${n.toString().length} digits`);
console.log(`φ(n) decimal length: ${phi.toString().length} digits`);
// Hexadecimal representation for easier reading
console.log('\n🔧 Hexadecimal Representation:');
console.log('━'.repeat(40));
console.log(`Prime p (hex): 0x${p.toString(16)}`);
console.log(`Prime q (hex): 0x${q.toString(16)}`);
console.log(`Modulus n (hex): 0x${n.toString(16)}`);
// Additional RSA parameters
const e = 65537n; // Standard RSA public exponent
console.log(`\n⚙️ Standard RSA public exponent e: ${e}`);
console.log(`e in hex: 0x${e.toString(16)}`);
console.log(`e in binary: 0b${e.toString(2)}`);
console.log('\n✅ RSA-1024 key pair generated successfully!');
console.log(`🔒 Ready for cryptographic operations with ${n.toString(2).length}-bit security`);Output:
🔐 RSA Key Pair Simulation
Generating two 512-bit primes for RSA...
RSA prime pair generation: 122.395ms
📊 Full RSA Key Parameters:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔢 Prime p (512 bits):
10332875180984937188212543009442021049052167852197686607297484227327378341552296925992226487617220177328455829145288166833100638955910093817267989178423073
🔢 Prime q (512 bits):
12954885855647726130509322447184318445832367546094918877784890443071291597566720274675251392103347355691843781436352664016291243646772976392340792132553717
🔐 Modulus n = p × q (1024 bits):
133861218530315201005653334393442917605123323330119927212727320055354313536219546299559853986366101719982021820444300266749246224830007993059434321470862538792295529692313615478696914663486817223475154407313734656489684823138730704549626389735179219724019378484222850588577807256333115082776522609570524712341
📈 Euler's totient φ(n) = (p-1)(q-1) (309 digits):
133861218530315201005653334393442917605123323330119927212727320055354313536219546299559853986366101719982021820444300266749246224830007993059434321470862515504534493059650296756831458037147322338939756114708249574115014424468791585532425722257299499156486358184612268947746957864450512399706313000789213735552
🎯 RSA Key Analysis:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Prime p bit length: 512 bits
Prime q bit length: 512 bits
Modulus n bit length: 1024 bits
Modulus n decimal length: 309 digits
φ(n) decimal length: 309 digits
🔧 Hexadecimal Representation:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Prime p (hex): 0xc54a0ab3eafc760aba80f137bd1ac7881fdd680e2319b6315d6e47f575bc2388940af890c439cbd614a7520448db93859095758797029ccbf8698f35ec5b1721
Prime q (hex): 0xf75a29bb758360582ab1f4ed5cd9f17c6272bbd90c4e0ebbad8e44370c4ed376a9c70ea43518e3a66506ccdbfc2abc7d35a46028fdbe2d649c56625befb4cbf5
Modulus n (hex): 0xbe9fec84ae56eabc59defd95a3e744f41fdd0ec934ff4068e4962756c3db675ac2b2f92e36a3a9cc2684df69b394429bc5c53840bfd618edf2acc98702f1282e46e73fb3e40648a0797634c86f7f3dabb4d62b58f8bbf2e0dcf7db05210547798a9b42005ec47d9dfc45c53245ba7d41ab77104b10cc4f583f7ae66680b84d95
⚙️ Standard RSA public exponent e: 65537
e in hex: 0x10001
e in binary: 0b10000000000000001
✅ RSA-1024 key pair generated successfully!
🔒 Ready for cryptographic operations with 1024-bit securityVerify the output using OpenSSL:
h0llyw00dzz@ubuntu-pro:~/Workspace/git/crypto-rand$ openssl prime 10332875180984937188212543009442021049052167852197686607297484227327378341552296925992226487617220177328455829145288166833100638955910093817267989178423073
C54A0AB3EAFC760ABA80F137BD1AC7881FDD680E2319B6315D6E47F575BC2388940AF890C439CBD614A7520448DB93859095758797029CCBF8698F35EC5B1721 (10332875180984937188212543009442021049052167852197686607297484227327378341552296925992226487617220177328455829145288166833100638955910093817267989178423073) is prime
h0llyw00dzz@ubuntu-pro:~/Workspace/git/crypto-rand$ openssl prime 12954885855647726130509322447184318445832367546094918877784890443071291597566720274675251392103347355691843781436352664016291243646772976392340792132553717
F75A29BB758360582AB1F4ED5CD9F17C6272BBD90C4E0EBBAD8E44370C4ED376A9C70EA43518E3A66506CCDBFC2ABC7D35A46028FDBE2D649C56625BEFB4CBF5 (12954885855647726130509322447184318445832367546094918877784890443071291597566720274675251392103347355691843781436352664016291243646772976392340792132553717) is prime[!TIP]
If you encounter an issue where the modulus ( n ) is not as expected (e.g., 1024 bits but returns 1023 bits), consider using this approach:
// Generate first RSA key pair let p: bigint, q: bigint, n: bigint, phi: bigint; const expectedBitLength: number = 512; // Loop to ensure modulus n is of the expected bit length do { p = Crypto.randPrime(expectedBitLength, 10, true); // Using enhanced FIPS mode q = Crypto.randPrime(expectedBitLength, 10, true); n = p * q; phi = (p - 1n) * (q - 1n); } while (n.toString(2).length !== 2 * expectedBitLength);Note that this issue, where the modulus ( n ) isn't as expected (e.g., 1024 bits but returns 1023 bits), is not a bug. In theory, "numbers don't lie."
To ensure high-quality ( p × q ), increase the number of iterations for accuracy. However, be aware that this may impact your system's performance, so adjust the iterations wisely.
For example, a high-quality ( p × q ):
🔢 Prime p (1024 bits): 93629667390763549743455182787551291607037005538469263456208785580579360237162918675053499364040380117827260438954037327068155571816415363590923149064296211316344684453342709572888762763763699420910422611609217694260666828600126970518675244226099301744993653235729128393490157011842848404542601183277427628913 🔢 Prime q (1024 bits): 175626859266695272885651840136370904489850577848971276776266672798504641918716678705774242052769822259100840653789506969497096025110479392809723327107911398990210101592275192909779675947860982696120429830662732823875953232932659348818061516003321491936390246622573145078330377015972795061320693588445635410027When computing ( n ):
🔐 Modulus n = p × q (2048 bits): 16443884418025117548105791962750907104057585305844601162650925309641405889906431436992904425679487416857606964858900739573861173341075086578451521650284489043240848823438572377480264841514770723125423683113631314524077817563403980472557462363541355254838641694785777889907988097731767993014399677529734388761633551331732914193006592599846235962565800326042232597361985089004996965000416284274426152928381037402336221116301394384508649690924628669942239923232626762521308841104446535709132972117182262279592274692864030418438345001588795102795792529507570109176579504027688756871881140850735912118414346948463155310651Another example, a high-quality ( p × q ):
🔢 Prime p (1024 bits): 176014562596357390158333092397129182747131938016638239747736142056577023945957931286910415334937793317150164468664828328536713918423550220286362297727001920879254154343039961363143416561274583220577956434429556558301467483983263366303873960104123130023249834442164293834519168997391367089035712289622382174403 🔢 Prime q (1024 bits): 119930918627410466541988914947061329063495175091760815095499653987838588298784040331571855576818400905503285794099893112575294836540726923671566101370094773992292750567966153158353521716094006708440846928281575787706279871556395999647706934659800908654843051127244668855876169326025470688413517187642268148587Another example, When computing ( n ):
🔐 Modulus n = p × q (2048 bits): 211095881839829840945377549792314505703824095546913007296489703767276608157231534980182576924214340081992771940481171057361197827387632587656088325559817767342461007405789174046304861739519735801494528030704614599508074040327966651206084073362117250464461423012474931138740439062492655671540675333671984055087211228056066044112993687648371945524402106661262082470154722117238385366952273385621008548643944464337948733671500820850172053876527980661277493548565047441604437171682960835720350494774336239940223907314524707878217528253719535635177111801041401147142638746650226314715769580368076876426420968713661520185614096-bits example, a high-quality ( p × q ):
🔢 Prime p (2048 bits): 31062935833744670225753257559781584892168049974923237166308591802942899178052323785187797613848187390473115506283534155453953753957368922081701939025938291302530161223440954615697682837569519072377763490122730717239109990856560566520263980030639103301828035437039566574992407831308781818375617131756581167712901303509794170698027964234104946896147613733492480298559243778383465787063236155400454403523384280500615450534811439978341820438429921957235236589380925080219431100747989349841765815311634595633347474235308351036240752956130598220135102264213182733681578342185601686831516174924099644103822508643127583476219 🔢 Prime q (2048 bits): 233750163355642755566093873022771942065318614120533326245286788401010683893001798835922113558917204008330313314736599821330087211073476953020475666993005959440163812489817127531380680190911647097184321928849650419110594235344405069104443837273304275028857385427707998976260283209058868589883771400467614733784656244584459971488402188334558790870450335323844648125408363619186192270616310616908620779050148042920004860083191735913413090400147944907413522315069758746411873140215479821502767908953896909942199619219421216015592423849482204501324942602633857414377709462395241254832807312179675610113454966673512776663374096-bits example, When computing ( n ):
🔐 Modulus n = p × q (4096 bits): 726096632544366566153678489161565601811710826109599759733820477841619019609006413571613336893871778668428174837829913569324413979378332098947468398483471090691876262459310688988945165647553502062898202858085751490527653434003054958254306911455190999549647054238300472755553127297160274612443847546317750556269968130584223313361557729640800852425926154866445555866597213943194857984320578772686820891207241958494959836360107006362512585312477954905571808971991757337392235767235105736784908269414760032532874260833272201068441334479479646488947341300067919621358909155432705610965196293350927658462181973020689470174767511503384485677253242429664297913416461644263497537863596487407649321620385351452785584734658599985141565852660758001007525219858642102492970522307669283582149953814827312034750480370244294093974006929022486039219953647945212214992256975983585925679600297179638521616796548107646941422149070263753846744876755357428662252261238463606769381821135061640577008402988054351763901237017333731945198396356474784835674551090886985359930885885249207857505309793927681691771842640180619282878716991472663897581613300903667145109140260748369101967704818457211889824317200092901133656189093076182635221617808138553372456339803Alternatively, if you want to minimize the impact on system performance, you can reduce the number of iterations. However, this approach depends more on luck to achieve a high-quality ( p × q ).
Security
This library uses Node.js's built-in crypto module to provide cryptographically secure random number generation. Unlike Math.random(), which uses a pseudorandom number generator that can be predictable, this library ensures true randomness suitable for security-sensitive applications.
Browser Compatibility
This package supports both Node.js and browser environments out of the box. It automatically detects the environment and uses the appropriate cryptographic API:
- Node.js: Uses the built-in
crypto.randomBytes()for secure random generation - Browser: Uses
window.crypto.getRandomValues()from the Web Crypto API - Universal: Works seamlessly in both environments without additional configuration
Supported Browsers
- Chrome 37+
- Firefox 34+
- Safari 7+
- Edge 12+
- Modern mobile browsers with Web Crypto API support
Performance
This library provides cryptographically secure random number generation with a focus on both security and performance. However, there are some important performance considerations to be aware of:
General Performance Considerations
Cryptographic Operations Overhead: Cryptographically secure random number generation is inherently more computationally intensive than non-secure alternatives like
Math.random(). This is a necessary trade-off for security.Synchronous vs. Asynchronous Methods: For most small operations, synchronous methods provide adequate performance. For larger operations or when generating multiple values, consider using asynchronous methods to avoid blocking the main thread.
Static Async Methods and Concurrency
Concurrency vs. Parallelism: While Node.js is single-threaded by nature, static async methods in this library guarantee concurrency but not 100% guarantee parallelism. This means:
- Multiple async operations can make progress concurrently within the event loop
- Operations don't block each other, improving overall throughput
- However, they don't execute in parallel on multiple CPU cores simultaneously
Event Loop Efficiency: Async methods allow the event loop to handle other tasks while waiting for cryptographic operations to complete, making your application more responsive.
Prime Number Generation Performance
- Key Size Recommendations:
- 2048-bit keys offer a good balance between security and performance for most applications. This is the recommended size for general use.
- 4096-bit keys, while providing stronger security, come with significant performance penalties (often 5-8x slower than 2048-bit operations) and are recommended only for highly sensitive applications where maximum security is required.
[!NOTE]
4096-bit keysare often 5-8 times slower than2048-bitoperations. This is not only during prime generation using probabilistic algorithms like Miller-Rabin, but also when performing encryption/decryption or signing/verifying operations.
- randPrime/randPrimeAsync Performance:
- Prime generation is computationally intensive, especially at larger bit sizes
- Using
randPrimeAsyncis strongly recommended for prime generation to avoid blocking the main thread
Performance Optimization Tips
- Use
Promise.all()with async methods when generating multiple random values - For RSA key generation, consider using 2048-bit keys unless you have specific security requirements
- When generating large amounts of random data, use the async methods and process the data in chunks
- ~~For performance-critical applications, consider implementing caching strategies for expensive operations like prime generation~~ -> not recommended; it's better to rely on entropy from cryptographically secure random number generators (CSPRNG).
